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
bool isOrfType(BasicIo& iIo, bool advance) { const int32_t len = 8; byte buf[len]; iIo.read(buf, len); if (iIo.error() || iIo.eof()) { return false; } OrfHeader orfHeader; bool rc = orfHeader.read(buf, len); if (!advance || !rc) { iIo.seek(-len, BasicIo::cur); } return rc; }
bool isJp2Type(BasicIo& iIo, bool advance) { const int32_t len = 12; byte buf[len]; iIo.read(buf, len); if (iIo.error() || iIo.eof()) { return false; } bool matched = (memcmp(buf, Jp2Signature, len) == 0); if (!advance || !matched) { iIo.seek(-len, BasicIo::cur); } return matched; }
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); int headerSize = (int) byteSwap_(buffer,0,bSwap_); if (headerSize <= 0 ) throw Error(22); #ifdef DEBUG std::cout << "Exiv2::PgfImage: PGF header size : " << headerSize << " bytes\n"; #endif return headerSize; } // PgfImage::readPgfHeaderSize
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; }
bool isAsfType(BasicIo& iIo, bool advance) { const int32_t len = 16; byte buf[len]; iIo.read(buf, len); if (iIo.error() || iIo.eof()) { return false; } bool matched = isASFType(buf); if (!advance || !matched) { iIo.seek(0, BasicIo::beg); } return matched; }
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
bool isBmpType(BasicIo& iIo, bool advance) { const int32_t len = 2; const unsigned char BmpImageId[2] = { 'B', 'M' }; byte buf[len]; iIo.read(buf, len); if (iIo.error() || iIo.eof()) { return false; } bool matched = (memcmp(buf, BmpImageId, len) == 0); if (!advance || !matched) { iIo.seek(-len, BasicIo::cur); } return matched; }
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
bool isPsdType(BasicIo& iIo, bool advance) { const int32_t len = 6; const unsigned char PsdHeader[6] = { '8', 'B', 'P', 'S', 0, 1 }; byte buf[len]; iIo.read(buf, len); if (iIo.error() || iIo.eof()) { return false; } bool matched = (memcmp(buf, PsdHeader, len) == 0); if (!advance || !matched) { iIo.seek(-len, BasicIo::cur); } return matched; }
bool isCrwType(BasicIo& iIo, bool advance) { bool result = true; byte tmpBuf[14]; iIo.read(tmpBuf, 14); if (iIo.error() || iIo.eof()) { return false; } if (!( ('I' == tmpBuf[0] && 'I' == tmpBuf[1]) || ('M' == tmpBuf[0] && 'M' == tmpBuf[1]))) { result = false; } if ( true == result && std::memcmp(tmpBuf + 6, CiffHeader::signature_, 8) != 0) { result = false; } if (!advance || !result) iIo.seek(-14, BasicIo::cur); return result; }
bool isGifType(BasicIo& iIo, bool advance) { const int32_t len = 6; const unsigned char Gif87aId[8] = { 'G', 'I', 'F', '8', '7', 'a' }; const unsigned char Gif89aId[8] = { 'G', 'I', 'F', '8', '9', 'a' }; byte buf[len]; iIo.read(buf, len); if (iIo.error() || iIo.eof()) { return false; } bool matched = (memcmp(buf, Gif87aId, len) == 0) || (memcmp(buf, Gif89aId, len) == 0); if (!advance || !matched) { iIo.seek(-len, BasicIo::cur); } return matched; }
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 = 16; long bufRead = 0; DataBuf buf(bufMinSize); const long seek = io_->tell(); 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(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 < 3) { // 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 (marker == app1_ && memcmp(buf.pData_ + 2, exifId_, 6) == 0) { if (size < 8) throw Error(22); skipApp1Exif = count; ++search; if (io_->seek(size-bufRead, BasicIo::cur)) throw Error(22); } else if (marker == app13_ && memcmp(buf.pData_ + 2, Photoshop::ps3Id_, 14) == 0) { if (size < 16) throw Error(22); skipApp13Ps3 = count; ++search; // needed if bufMinSize!=16: io_->seek(16-bufRead, BasicIo::cur); psData.alloc(size - 16); // Load PS data now to allow reinsertion at any point io_->read(psData.pData_, psData.size_); 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); } marker = advanceToMarker(); if (marker < 0) throw Error(22); ++count; } if (exifData_.count() > 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[18]; if (!comment_.empty()) { // 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; } if (exifData_.count() > 0) { DataBuf rawExif = exifData_.copy(); if (rawExif.size_ > 0) { // Write APP1 marker, size of APP1 field, Exif id and Exif data tmpBuf[0] = 0xff; tmpBuf[1] = app1_; if (rawExif.size_ + 8 > 0xffff) throw Error(37, "Exif"); us2Data(tmpBuf + 2, static_cast<uint16_t>(rawExif.size_ + 8), bigEndian); memcpy(tmpBuf + 4, exifId_, 6); if (outIo.write(tmpBuf, 10) != 10) throw Error(21); // Write new Exif data buffer if ( outIo.write(rawExif.pData_, rawExif.size_) != rawExif.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); 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 (marker == eoi_) { break; } else if (skipApp1Exif==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 FileIo::transfer(BasicIo& src) { const bool wasOpen = (p_->fp_ != 0); const std::string lastMode(p_->openMode_); FileIo *fileIo = dynamic_cast<FileIo*>(&src); if (fileIo) { // Optimization if src is another instance of FileIo fileIo->close(); // Check if the file can be written to, if it already exists if (open("a+b") != 0) { // Remove the (temporary) file #ifdef EXV_UNICODE_PATH if (fileIo->p_->wpMode_ == Impl::wpUnicode) { ::_wremove(fileIo->wpath().c_str()); } else #endif { ::remove(fileIo->path().c_str()); } #ifdef EXV_UNICODE_PATH if (p_->wpMode_ == Impl::wpUnicode) { throw WError(10, wpath(), "a+b", strError().c_str()); } else #endif { throw Error(10, path(), "a+b", strError()); } } close(); bool statOk = true; mode_t origStMode = 0; std::string spf; char* pf = 0; #ifdef EXV_UNICODE_PATH std::wstring wspf; wchar_t* wpf = 0; if (p_->wpMode_ == Impl::wpUnicode) { wspf = wpath(); wpf = const_cast<wchar_t*>(wspf.c_str()); } else #endif { spf = path(); pf = const_cast<char*>(spf.c_str()); } // Get the permissions of the file, or linked-to file, on platforms which have lstat #ifdef EXV_HAVE_LSTAT # ifdef EXV_UNICODE_PATH # error EXV_UNICODE_PATH and EXV_HAVE_LSTAT are not compatible. Stop. # endif struct stat buf1; if (::lstat(pf, &buf1) == -1) { statOk = false; #ifndef SUPPRESS_WARNINGS EXV_WARNING << Error(2, pf, strError(), "::lstat") << "\n"; #endif } origStMode = buf1.st_mode; DataBuf lbuf; // So that the allocated memory is freed. Must have same scope as pf // In case path() is a symlink, get the path of the linked-to file if (statOk && S_ISLNK(buf1.st_mode)) { lbuf.alloc(buf1.st_size + 1); memset(lbuf.pData_, 0x0, lbuf.size_); pf = reinterpret_cast<char*>(lbuf.pData_); if (::readlink(path().c_str(), pf, lbuf.size_ - 1) == -1) { throw Error(2, path(), strError(), "readlink"); } // We need the permissions of the file, not the symlink if (::stat(pf, &buf1) == -1) { statOk = false; #ifndef SUPPRESS_WARNINGS EXV_WARNING << Error(2, pf, strError(), "::stat") << "\n"; #endif } origStMode = buf1.st_mode; } #else // EXV_HAVE_LSTAT Impl::StructStat buf1; if (p_->stat(buf1) == -1) { statOk = false; } origStMode = buf1.st_mode; #endif // !EXV_HAVE_LSTAT // MSVCRT rename that does not overwrite existing files #ifdef EXV_UNICODE_PATH if (p_->wpMode_ == Impl::wpUnicode) { if (fileExists(wpf) && ::_wremove(wpf) != 0) { throw WError(2, wpf, strError().c_str(), "::_wremove"); } if (::_wrename(fileIo->wpath().c_str(), wpf) == -1) { throw WError(17, fileIo->wpath(), wpf, strError().c_str()); } ::_wremove(fileIo->wpath().c_str()); // Check permissions of new file struct _stat buf2; if (statOk && ::_wstat(wpf, &buf2) == -1) { statOk = false; #ifndef SUPPRESS_WARNINGS EXV_WARNING << Error(2, wpf, strError(), "::_wstat") << "\n"; #endif } if (statOk && origStMode != buf2.st_mode) { // Set original file permissions if (::_wchmod(wpf, origStMode) == -1) { #ifndef SUPPRESS_WARNINGS EXV_WARNING << Error(2, wpf, strError(), "::_wchmod") << "\n"; #endif } } } // if (p_->wpMode_ == Impl::wpUnicode) else #endif // EXV_UNICODE_PATH { if (fileExists(pf) && ::remove(pf) != 0) { throw Error(2, pf, strError(), "::remove"); } if (::rename(fileIo->path().c_str(), pf) == -1) { throw Error(17, fileIo->path(), pf, strError()); } ::remove(fileIo->path().c_str()); // Check permissions of new file struct stat buf2; if (statOk && ::stat(pf, &buf2) == -1) { statOk = false; #ifndef SUPPRESS_WARNINGS EXV_WARNING << Error(2, pf, strError(), "::stat") << "\n"; #endif } if (statOk && origStMode != buf2.st_mode) { // Set original file permissions if (::chmod(pf, origStMode) == -1) { #ifndef SUPPRESS_WARNINGS EXV_WARNING << Error(2, pf, strError(), "::chmod") << "\n"; #endif } } } } // if (fileIo) else { // Generic handling, reopen both to reset to start if (open("w+b") != 0) { #ifdef EXV_UNICODE_PATH if (p_->wpMode_ == Impl::wpUnicode) { throw WError(10, wpath(), "w+b", strError().c_str()); } else #endif { throw Error(10, path(), "w+b", strError()); } } if (src.open() != 0) { #ifdef EXV_UNICODE_PATH if (p_->wpMode_ == Impl::wpUnicode) { throw WError(9, src.wpath(), strError().c_str()); } else #endif { throw Error(9, src.path(), strError()); } } write(src); src.close(); } if (wasOpen) { if (open(lastMode) != 0) { #ifdef EXV_UNICODE_PATH if (p_->wpMode_ == Impl::wpUnicode) { throw WError(10, wpath(), lastMode.c_str(), strError().c_str()); } else #endif { throw Error(10, path(), lastMode, strError()); } } } else close(); if (error() || src.error()) { #ifdef EXV_UNICODE_PATH if (p_->wpMode_ == Impl::wpUnicode) { throw WError(18, wpath(), strError().c_str()); } else #endif { throw Error(18, path(), strError()); } } } // FileIo::transfer
void PsdImage::doWriteMetadata(BasicIo& outIo) { if (!io_->isopen()) throw Error(20); if (!outIo.isopen()) throw Error(21); #ifdef DEBUG std::cout << "Exiv2::PsdImage::doWriteMetadata: Writing PSD file " << io_->path() << "\n"; std::cout << "Exiv2::PsdImage::doWriteMetadata: tmp file created " << outIo.path() << "\n"; #endif // Ensure that this is the correct image type if (!isPsdType(*io_, true)) { if (io_->error() || io_->eof()) throw Error(20); throw Error(22); } io_->seek(0, BasicIo::beg); // rewind DataBuf lbuf(4096); byte buf[8]; // Get Photoshop header from original file byte psd_head[26]; if (io_->read(psd_head, 26) != 26) throw Error(3, "Photoshop"); // Write Photoshop header data out to new PSD file if (outIo.write(psd_head, 26) != 26) throw Error(21); // Read colorDataLength from original PSD if (io_->read(buf, 4) != 4) throw Error(3, "Photoshop"); uint32_t colorDataLength = getULong(buf, bigEndian); // Write colorDataLength ul2Data(buf, colorDataLength, bigEndian); if (outIo.write(buf, 4) != 4) throw Error(21); #ifdef DEBUG std::cerr << std::dec << "colorDataLength: " << colorDataLength << "\n"; #endif // Copy colorData uint32_t readTotal = 0; long toRead = 0; while (readTotal < colorDataLength) { toRead = static_cast<long>(colorDataLength - readTotal) < lbuf.size_ ? colorDataLength - readTotal : lbuf.size_; if (io_->read(lbuf.pData_, toRead) != toRead) throw Error(3, "Photoshop"); readTotal += toRead; if (outIo.write(lbuf.pData_, toRead) != toRead) throw Error(21); } if (outIo.error()) throw Error(21); uint32_t resLenOffset = io_->tell(); // remember for later update // Read length of all resource blocks from original PSD if (io_->read(buf, 4) != 4) throw Error(3, "Photoshop"); uint32_t oldResLength = getULong(buf, bigEndian); uint32_t newResLength = 0; // Write oldResLength (will be updated later) ul2Data(buf, oldResLength, bigEndian); if (outIo.write(buf, 4) != 4) throw Error(21); #ifdef DEBUG std::cerr << std::dec << "oldResLength: " << oldResLength << "\n"; #endif // Iterate over original resource blocks. // Replace or insert IPTC, EXIF and XMP // Original resource blocks assumed to be sorted ASC bool iptcDone = false; bool exifDone = false; bool xmpDone = false; while (oldResLength > 0) { if (io_->read(buf, 8) != 8) throw Error(3, "Photoshop"); // read resource type and ID uint32_t resourceType = getULong(buf, bigEndian); if (resourceType != kPhotoshopResourceType) { break; // bad resource type } uint16_t resourceId = getUShort(buf + 4, bigEndian); uint32_t resourceNameLength = buf[6]; uint32_t adjResourceNameLen = resourceNameLength & ~1; unsigned char resourceNameFirstChar = buf[7]; // read rest of resource name, plus any padding DataBuf resName(256); if ( io_->read(resName.pData_, adjResourceNameLen) != static_cast<long>(adjResourceNameLen)) throw Error(3, "Photoshop"); // read resource size (actual length w/o padding!) if (io_->read(buf, 4) != 4) throw Error(3, "Photoshop"); uint32_t resourceSize = getULong(buf, bigEndian); uint32_t curOffset = io_->tell(); // Write IPTC_NAA resource block if ( resourceId == kPhotoshopResourceID_IPTC_NAA || (resourceId > kPhotoshopResourceID_IPTC_NAA && iptcDone == false)) { newResLength += writeIptcData(iptcData_, outIo); resourceSize = (resourceSize + 1) & ~1; // adjust for padding iptcDone = true; } // Write ExifInfo resource block else if ( resourceId == kPhotoshopResourceID_ExifInfo || (resourceId > kPhotoshopResourceID_ExifInfo && exifDone == false)) { newResLength += writeExifData(exifData_, outIo); resourceSize = (resourceSize + 1) & ~1; // adjust for padding exifDone = true; } // Write XMPpacket resource block else if ( resourceId == kPhotoshopResourceID_XMPPacket || (resourceId > kPhotoshopResourceID_XMPPacket && xmpDone == false)) { newResLength += writeXmpData(xmpData_, outIo); resourceSize = (resourceSize + 1) & ~1; // adjust for padding xmpDone = true; } // Copy all other resource blocks if ( resourceId != kPhotoshopResourceID_IPTC_NAA && resourceId != kPhotoshopResourceID_ExifInfo && resourceId != kPhotoshopResourceID_XMPPacket) { #ifdef DEBUG std::cerr << std::hex << "copy : resourceId: " << resourceId << "\n"; std::cerr << std::dec; #endif // Copy resource block to new PSD file ul2Data(buf, kPhotoshopResourceType, bigEndian); if (outIo.write(buf, 4) != 4) throw Error(21); us2Data(buf, resourceId, bigEndian); if (outIo.write(buf, 2) != 2) throw Error(21); // Write resource name as Pascal string buf[0] = resourceNameLength & 0x000f; if (outIo.write(buf, 1) != 1) throw Error(21); buf[0] = resourceNameFirstChar; 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"); } readTotal += toRead; if (outIo.write(lbuf.pData_, toRead) != toRead) throw Error(21); } if (outIo.error()) throw Error(21); newResLength += resourceSize + adjResourceNameLen + 12; } io_->seek(curOffset + resourceSize, BasicIo::beg); 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 long readSize = 0; while ((readSize=io_->read(lbuf.pData_, lbuf.size_))) { if (outIo.write(lbuf.pData_, readSize) != readSize) throw Error(21); } if (outIo.error()) throw Error(21); // Update length of resources #ifdef DEBUG std::cerr << "newResLength: " << newResLength << "\n"; #endif outIo.seek(resLenOffset, BasicIo::beg); ul2Data(buf, newResLength, bigEndian); if (outIo.write(buf, 4) != 4) throw Error(21); } // PsdImage::doWriteMetadata
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); byteSwap_(buffer,0,bSwap_); 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
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 FileIo::transfer(BasicIo& src) { const bool wasOpen = (fp_ != 0); const std::string lastMode(openMode_); FileIo *fileIo = dynamic_cast<FileIo*>(&src); if (fileIo) { // Optimization if src is another instance of FileIo fileIo->close(); // Check if the file can be written to, if it already exists if (open("w+b") != 0) { // Remove the (temporary) file std::remove(fileIo->path_.c_str()); throw Error(10, path_, "w+b", strError()); } close(); bool statOk = true; struct stat buf1; if (::stat(path_.c_str(), &buf1) == -1) { statOk = false; #ifndef SUPPRESS_WARNINGS std::cerr << "Warning: " << Error(2, path_, strError(), "stat") << "\n"; #endif } // MSVCRT rename that does not overwrite existing files if (fileExists(path_) && std::remove(path_.c_str()) != 0) { throw Error(2, path_, strError(), "std::remove"); } if (std::rename(fileIo->path_.c_str(), path_.c_str()) == -1) { throw Error(17, fileIo->path_, path_, strError()); } std::remove(fileIo->path_.c_str()); // Check permissions of new file struct stat buf2; if (statOk && ::stat(path_.c_str(), &buf2) == -1) { statOk = false; #ifndef SUPPRESS_WARNINGS std::cerr << "Warning: " << Error(2, path_, strError(), "stat") << "\n"; #endif } if (statOk && buf1.st_mode != buf2.st_mode) { // Set original file permissions if (::chmod(path_.c_str(), buf1.st_mode) == -1) { #ifndef SUPPRESS_WARNINGS std::cerr << "Warning: " << Error(2, path_, strError(), "chmod") << "\n"; #endif } } } else { // Generic handling, reopen both to reset to start if (open("w+b") != 0) { throw Error(10, path_, "w+b", strError()); } if (src.open() != 0) { throw Error(9, src.path(), strError()); } write(src); src.close(); } if (wasOpen) { if (open(lastMode) != 0) { throw Error(10, path_, lastMode, strError()); } } else close(); if (error() || src.error()) throw Error(18, path_, strError()); }
void FileIo::transfer(BasicIo& src) { const bool wasOpen = (fp_ != 0); const std::string lastMode(openMode_); FileIo *fileIo = dynamic_cast<FileIo*>(&src); if (fileIo) { // Optimization if src is another instance of FileIo fileIo->close(); // Check if the file can be written to, if it already exists if (open("w+b") != 0) { // Remove the (temporary) file std::remove(fileIo->path_.c_str()); throw Error(10, path_, "w+b", strError()); } close(); bool statOk = true; struct stat buf1; char* pf = const_cast<char*>(path_.c_str()); #ifdef EXV_HAVE_LSTAT if (::lstat(pf, &buf1) == -1) { statOk = false; #ifndef SUPPRESS_WARNINGS std::cerr << "Warning: " << Error(2, pf, strError(), "lstat") << "\n"; #endif } DataBuf lbuf; // So that the allocated memory is freed. Must have same scope as pf // In case path_ is a symlink, get the path of the linked-to file if (statOk && S_ISLNK(buf1.st_mode)) { lbuf.alloc(buf1.st_size + 1); memset(lbuf.pData_, 0x0, lbuf.size_); pf = reinterpret_cast<char*>(lbuf.pData_); if (readlink(path_.c_str(), pf, lbuf.size_ - 1) == -1) { throw Error(2, path_, strError(), "readlink"); } // We need the permissions of the file, not the symlink if (::stat(pf, &buf1) == -1) { statOk = false; #ifndef SUPPRESS_WARNINGS std::cerr << "Warning: " << Error(2, pf, strError(), "stat") << "\n"; #endif } } #else if (::stat(pf, &buf1) == -1) { statOk = false; #ifndef SUPPRESS_WARNINGS std::cerr << "Warning: " << Error(2, pf, strError(), "stat") << "\n"; #endif } #endif // !EXV_HAVE_LSTAT // MSVCRT rename that does not overwrite existing files if (fileExists(pf) && std::remove(pf) != 0) { throw Error(2, pf, strError(), "std::remove"); } if (std::rename(fileIo->path_.c_str(), pf) == -1) { throw Error(17, fileIo->path_, pf, strError()); } std::remove(fileIo->path_.c_str()); // Check permissions of new file struct stat buf2; if (statOk && ::stat(pf, &buf2) == -1) { statOk = false; #ifndef SUPPRESS_WARNINGS std::cerr << "Warning: " << Error(2, pf, strError(), "stat") << "\n"; #endif } if (statOk && buf1.st_mode != buf2.st_mode) { // Set original file permissions if (::chmod(pf, buf1.st_mode) == -1) { #ifndef SUPPRESS_WARNINGS std::cerr << "Warning: " << Error(2, pf, strError(), "chmod") << "\n"; #endif } } } else { // Generic handling, reopen both to reset to start if (open("w+b") != 0) { throw Error(10, path_, "w+b", strError()); } if (src.open() != 0) { throw Error(9, src.path(), strError()); } write(src); src.close(); } if (wasOpen) { if (open(lastMode) != 0) { throw Error(10, path_, lastMode, strError()); } } else close(); if (error() || src.error()) throw Error(18, path_, strError()); }