bool isTgaType(BasicIo& iIo, bool /*advance*/) { // not all TARGA files have a signature string, so first just try to match the file name extension std::string path = iIo.path(); if(path.rfind(".tga") != std::string::npos || path.rfind(".TGA") != std::string::npos) { return true; } byte buf[26]; long curPos = iIo.tell(); iIo.seek(-26, BasicIo::end); if (iIo.error() || iIo.eof()) { return false; } iIo.read(buf, sizeof(buf)); if (iIo.error()) { return false; } // some TARGA files, but not all, have a signature string at the end bool matched = (memcmp(buf + 8, "TRUEVISION-XFILE", 16) == 0); iIo.seek(curPos, BasicIo::beg); return matched; }
void MemIo::transfer(BasicIo& src) { MemIo *memIo = dynamic_cast<MemIo*>(&src); if (memIo) { // Optimization if src is another instance of MemIo if (true == isMalloced_) { std::free(data_); } idx_ = 0; data_ = memIo->data_; size_ = memIo->size_; isMalloced_ = memIo->isMalloced_; memIo->idx_ = 0; memIo->data_ = 0; memIo->size_ = 0; memIo->isMalloced_ = false; } else { // Generic reopen to reset position to start if (src.open() != 0) { throw Error(9, src.path(), strError()); } idx_ = 0; write(src); src.close(); } if (error() || src.error()) throw Error(19, 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(); struct stat buf; if (::stat(path_.c_str(), &buf) == -1) { throw Error(2, path_, strError(), "stat"); } // 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()); // Set original file permissions if (::chmod(path_.c_str(), buf.st_mode) == -1) { throw Error(2, fileIo->path_, strError(), "chmod"); } } 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 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 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 PngImage::doWriteMetadata(BasicIo& outIo) { if (!io_->isopen()) throw Error(20); if (!outIo.isopen()) throw Error(21); #ifdef DEBUG std::cout << "Exiv2::PngImage::doWriteMetadata: Writing PNG file " << io_->path() << "\n"; std::cout << "Exiv2::PngImage::doWriteMetadata: tmp file created " << outIo.path() << "\n"; #endif // Ensure that this is the correct image type if (!isPngType(*io_, true)) { if (io_->error() || io_->eof()) throw Error(20); throw Error(22); } // Write PNG Signature. if (outIo.write(pngSignature, 8) != 8) throw Error(21); DataBuf cheaderBuf(8); // Chunk header : 4 bytes (data size) + 4 bytes (chunk type). while(!io_->eof()) { // Read chunk header. std::memset(cheaderBuf.pData_, 0x00, cheaderBuf.size_); long bufRead = io_->read(cheaderBuf.pData_, cheaderBuf.size_); if (io_->error()) throw Error(14); if (bufRead != cheaderBuf.size_) throw Error(20); // Decode chunk data length. uint32_t dataOffset = getULong(cheaderBuf.pData_, bigEndian); if (dataOffset > 0x7FFFFFFF) throw Exiv2::Error(14); // Read whole chunk : Chunk header + Chunk data (not fixed size - can be null) + CRC (4 bytes). DataBuf chunkBuf(8 + dataOffset + 4); // Chunk header (8 bytes) + Chunk data + CRC (4 bytes). memcpy(chunkBuf.pData_, cheaderBuf.pData_, 8); // Copy header. bufRead = io_->read(chunkBuf.pData_ + 8, dataOffset + 4); // Extract chunk data + CRC if (io_->error()) throw Error(14); if (bufRead != (long)(dataOffset + 4)) throw Error(20); if (!memcmp(cheaderBuf.pData_ + 4, "IEND", 4)) { // Last chunk found: we write it and done. #ifdef DEBUG std::cout << "Exiv2::PngImage::doWriteMetadata: Write IEND chunk (lenght: " << dataOffset << ")\n"; #endif if (outIo.write(chunkBuf.pData_, chunkBuf.size_) != chunkBuf.size_) throw Error(21); return; } else if (!memcmp(cheaderBuf.pData_ + 4, "IHDR", 4)) { #ifdef DEBUG std::cout << "Exiv2::PngImage::doWriteMetadata: Write IHDR chunk (lenght: " << dataOffset << ")\n"; #endif if (outIo.write(chunkBuf.pData_, chunkBuf.size_) != chunkBuf.size_) throw Error(21); // Write all updated metadata here, just after IHDR. if (!comment_.empty()) { // Update Comment data to a new compressed iTXt PNG chunk DataBuf com(reinterpret_cast<const byte*>(comment_.data()), static_cast<long>(comment_.size())); DataBuf chunkData = PngChunk::makeMetadataChunk(com, PngChunk::comment_Data, true); #ifdef DEBUG std::cout << "Exiv2::PngImage::doWriteMetadata: Write chunk with Comment metadata (lenght: " << chunkData.size_ << ")\n"; #endif if (outIo.write(chunkData.pData_, chunkData.size_) != chunkData.size_) throw Error(21); } if (exifData_.count() > 0) { // Update Exif data to a new zTXt PNG chunk Blob blob; ExifParser::encode(blob, littleEndian, exifData_); if (blob.size()) { const unsigned char ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00}; DataBuf rawExif(sizeof(ExifHeader) + blob.size()); memcpy(rawExif.pData_, ExifHeader, sizeof(ExifHeader)); memcpy(rawExif.pData_ + sizeof(ExifHeader), &blob[0], blob.size()); DataBuf chunkData = PngChunk::makeMetadataChunk(rawExif, PngChunk::exif_Data, true); #ifdef DEBUG std::cout << "Exiv2::PngImage::doWriteMetadata: Write chunk with Exif metadata (lenght: " << chunkData.size_ << ")\n"; #endif if (outIo.write(chunkData.pData_, chunkData.size_) != chunkData.size_) throw Error(21); } } if (iptcData_.count() > 0) { // Update Iptc data to a new zTXt PNG chunk DataBuf rawIptc = IptcParser::encode(iptcData_); if (rawIptc.size_ > 0) { DataBuf chunkData = PngChunk::makeMetadataChunk(rawIptc, PngChunk::iptc_Data, true); #ifdef DEBUG std::cout << "Exiv2::PngImage::doWriteMetadata: Write chunk with Iptc metadata (lenght: " << chunkData.size_ << ")\n"; #endif if (outIo.write(chunkData.pData_, chunkData.size_) != chunkData.size_) throw Error(21); } } if (writeXmpFromPacket() == false) { if (XmpParser::encode(xmpPacket_, xmpData_)) { #ifndef SUPPRESS_WARNINGS std::cerr << "Error: Failed to encode XMP metadata.\n"; #endif } } if (xmpPacket_.size() > 0) { // Update Xmp data to a new uncompressed iTXt PNG chunk // Note than XMP spec. Ver September 2005, page 97 require an uncompressed chunk to host XMP data DataBuf xmp(reinterpret_cast<const byte*>(xmpPacket_.data()), static_cast<long>(xmpPacket_.size())); DataBuf chunkData = PngChunk::makeMetadataChunk(xmp, PngChunk::xmp_Data, false); #ifdef DEBUG std::cout << "Exiv2::PngImage::doWriteMetadata: Write chunk with XMP metadata (lenght: " << chunkData.size_ << ")\n"; #endif if (outIo.write(chunkData.pData_, chunkData.size_) != chunkData.size_) throw Error(21); } } else if (!memcmp(cheaderBuf.pData_ + 4, "tEXt", 4) || !memcmp(cheaderBuf.pData_ + 4, "zTXt", 4) || !memcmp(cheaderBuf.pData_ + 4, "iTXt", 4)) { DataBuf key = PngChunk::keyTXTChunk(chunkBuf, true); if (memcmp("Raw profile type exif", key.pData_, 21) == 0 || memcmp("Raw profile type APP1", key.pData_, 21) == 0 || memcmp("Raw profile type iptc", key.pData_, 21) == 0 || memcmp("Raw profile type xmp", key.pData_, 20) == 0 || memcmp("XML:com.adobe.xmp", key.pData_, 17) == 0 || memcmp("Description", key.pData_, 11) == 0) { #ifdef DEBUG std::cout << "Exiv2::PngImage::doWriteMetadata: strip " << cheaderBuf.pData_ + 4 << " chunk (key: " << key.pData_ << ")\n"; #endif } else { #ifdef DEBUG std::cout << "Exiv2::PngImage::doWriteMetadata: write " << cheaderBuf.pData_ + 4 << " chunk (lenght: " << dataOffset << ")\n"; #endif if (outIo.write(chunkBuf.pData_, chunkBuf.size_) != chunkBuf.size_) throw Error(21); } } else { // Write all others chunk as well. #ifdef DEBUG std::cout << "Exiv2::PngImage::doWriteMetadata: write " << cheaderBuf.pData_ + 4 << " chunk (lenght: " << dataOffset << ")\n"; #endif if (outIo.write(chunkBuf.pData_, chunkBuf.size_) != chunkBuf.size_) throw Error(21); } } } // PngImage::doWriteMetadata
int WriteReadSeek(BasicIo &io) { byte buf[4096]; const char tester1[] = "this is a little test of MemIo"; const char tester2[] = "Appending this on the end"; const char expect[] = "this is a little teAppending this on the end"; const long insert = 19; const long len1 = (long)std::strlen(tester1) + 1; const long len2 = (long)std::strlen(tester2) + 1; if (io.open() != 0) { throw Error(9, io.path(), strError()); } IoCloser closer(io); if (io.write((byte*)tester1, len1) != len1) { std::cerr << ": WRS initial write failed\n"; return 2; } if (io.size() != len1) { std::cerr << ": WRS size is not " << len1 << "\n"; return 2; } io.seek(-len1, BasicIo::cur); int c = EOF; std::memset(buf, -1, sizeof(buf)); for (int i = 0; (c=io.getb()) != EOF; ++i) { buf[i] = (byte)c; } // Make sure we got the null back if(buf[len1-1] != 0) { std::cerr << ": WRS missing null terminator 1\n"; return 3; } if (strcmp(tester1, (char*)buf) != 0 ) { std::cerr << ": WRS strings don't match 1\n"; return 4; } io.seek(-2, BasicIo::end); if (io.getb() != 'o') { std::cerr << ": WRS bad getb o\n"; return 5; } io.seek(-2, BasicIo::cur); if (io.getb() != 'I') { std::cerr << ": WRS bad getb I\n"; return 6; } if (io.putb('O') != 'O') { std::cerr << ": WRS bad putb\n"; return 7; } io.seek(-1, BasicIo::cur); if (io.getb() != 'O') { std::cerr << ": WRS bad getb O\n"; return 8; } io.seek(insert, BasicIo::beg); if(io.write((byte*)tester2, len2) != len2) { std::cerr << ": WRS bad write 1\n"; return 9; } // open should seek to beginning if (io.open() != 0) { throw Error(9, io.path(), strError()); } std::memset(buf, -1, sizeof(buf)); if (io.read(buf, sizeof(buf)) != insert + len2) { std::cerr << ": WRS something went wrong\n"; return 10; } // Make sure we got the null back if(buf[insert + len2 - 1] != 0) { std::cerr << ": WRS missing null terminator 2\n"; return 11; } if (std::strcmp(expect, (char*)buf) != 0 ) { std::cerr << ": WRS strings don't match 2\n"; return 12; } return 0; }
void PngImage::doWriteMetadata(BasicIo& outIo) { if (!io_->isopen()) throw Error(20); if (!outIo.isopen()) throw Error(21); #ifdef DEBUG std::cout << "Exiv2::PngImage::doWriteMetadata: Writing PNG file " << io_->path() << "\n"; std::cout << "Exiv2::PngImage::doWriteMetadata: tmp file created " << outIo.path() << "\n"; #endif // Ensure that this is the correct image type if (!isPngType(*io_, true)) { if (io_->error() || io_->eof()) throw Error(20); throw Error(22); } // Write PNG Signature. if (outIo.write(pngSignature, 8) != 8) throw Error(21); DataBuf cheaderBuf(8); // Chunk header : 4 bytes (data size) + 4 bytes (chunk type). while(!io_->eof()) { // Read chunk header. std::memset(cheaderBuf.pData_, 0x00, cheaderBuf.size_); long bufRead = io_->read(cheaderBuf.pData_, cheaderBuf.size_); if (io_->error()) throw Error(14); if (bufRead != cheaderBuf.size_) throw Error(20); // Decode chunk data length. uint32_t dataOffset = getULong(cheaderBuf.pData_, bigEndian); if (dataOffset > 0x7FFFFFFF) throw Exiv2::Error(14); // Read whole chunk : Chunk header + Chunk data (not fixed size - can be null) + CRC (4 bytes). DataBuf chunkBuf(8 + dataOffset + 4); // Chunk header (8 bytes) + Chunk data + CRC (4 bytes). memcpy(chunkBuf.pData_, cheaderBuf.pData_, 8); // Copy header. bufRead = io_->read(chunkBuf.pData_ + 8, dataOffset + 4); // Extract chunk data + CRC if (io_->error()) throw Error(14); if (bufRead != (long)(dataOffset + 4)) throw Error(20); if (!memcmp(cheaderBuf.pData_ + 4, "IEND", 4)) { // Last chunk found: we write it and done. #ifdef DEBUG std::cout << "Exiv2::PngImage::doWriteMetadata: Write IEND chunk (length: " << dataOffset << ")\n"; #endif if (outIo.write(chunkBuf.pData_, chunkBuf.size_) != chunkBuf.size_) throw Error(21); return; } else if (!memcmp(cheaderBuf.pData_ + 4, "IHDR", 4)) { #ifdef DEBUG std::cout << "Exiv2::PngImage::doWriteMetadata: Write IHDR chunk (length: " << dataOffset << ")\n"; #endif if (outIo.write(chunkBuf.pData_, chunkBuf.size_) != chunkBuf.size_) throw Error(21); // Write all updated metadata here, just after IHDR. if (!comment_.empty()) { // Update Comment data to a new PNG chunk std::string chunk = PngChunk::makeMetadataChunk(comment_, mdComment); if (outIo.write((const byte*)chunk.data(), static_cast<long>(chunk.size())) != (long)chunk.size()) { throw Error(21); } } if (exifData_.count() > 0) { // Update Exif data to a new PNG chunk Blob blob; ExifParser::encode(blob, littleEndian, exifData_); if (blob.size() > 0) { static const char exifHeader[] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 }; std::string rawExif = std::string(exifHeader, 6) + std::string((const char*)&blob[0], blob.size()); std::string chunk = PngChunk::makeMetadataChunk(rawExif, mdExif); if (outIo.write((const byte*)chunk.data(), static_cast<long>(chunk.size())) != (long)chunk.size()) { throw Error(21); } } } if (iptcData_.count() > 0) { // Update IPTC data to a new PNG chunk DataBuf newPsData = Photoshop::setIptcIrb(0, 0, iptcData_); if (newPsData.size_ > 0) { std::string rawIptc((const char*)newPsData.pData_, newPsData.size_); std::string chunk = PngChunk::makeMetadataChunk(rawIptc, mdIptc); if (outIo.write((const byte*)chunk.data(), static_cast<long>(chunk.size())) != (long)chunk.size()) { throw Error(21); } } } 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) { // Update XMP data to a new PNG chunk std::string chunk = PngChunk::makeMetadataChunk(xmpPacket_, mdXmp); if (outIo.write((const byte*)chunk.data(), static_cast<long>(chunk.size())) != (long)chunk.size()) { throw Error(21); } } } else if (!memcmp(cheaderBuf.pData_ + 4, "tEXt", 4) || !memcmp(cheaderBuf.pData_ + 4, "zTXt", 4) || !memcmp(cheaderBuf.pData_ + 4, "iTXt", 4)) { DataBuf key = PngChunk::keyTXTChunk(chunkBuf, true); if (memcmp("Raw profile type exif", key.pData_, 21) == 0 || memcmp("Raw profile type APP1", key.pData_, 21) == 0 || memcmp("Raw profile type iptc", key.pData_, 21) == 0 || memcmp("Raw profile type xmp", key.pData_, 20) == 0 || memcmp("XML:com.adobe.xmp", key.pData_, 17) == 0 || memcmp("Description", key.pData_, 11) == 0) { #ifdef DEBUG std::cout << "Exiv2::PngImage::doWriteMetadata: strip " << cheaderBuf.pData_ + 4 << " chunk (key: " << key.pData_ << ")\n"; #endif } else { #ifdef DEBUG std::cout << "Exiv2::PngImage::doWriteMetadata: write " << cheaderBuf.pData_ + 4 << " chunk (length: " << dataOffset << ")\n"; #endif if (outIo.write(chunkBuf.pData_, chunkBuf.size_) != chunkBuf.size_) throw Error(21); } } else { // Write all others chunk as well. #ifdef DEBUG std::cout << "Exiv2::PngImage::doWriteMetadata: write " << cheaderBuf.pData_ + 4 << " chunk (length: " << dataOffset << ")\n"; #endif if (outIo.write(chunkBuf.pData_, chunkBuf.size_) != chunkBuf.size_) throw Error(21); } } } // PngImage::doWriteMetadata
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 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 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()); }