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; }
bool isXmpType(BasicIo& iIo, bool advance) { /* Make sure the file starts with and (optional) XML declaration, followed by an XMP header (<?xpacket ... ?>) or an <x:xmpmeta> element. That doesn't cover all cases, since also x:xmpmeta is optional, but let's wait and see. */ // Todo: Proper implementation const int32_t len = 10; byte buf[len]; iIo.read(buf, len); if (iIo.error() || iIo.eof()) { return false; } bool rc = false; const std::string head(reinterpret_cast<const char*>(buf), len); if ( head.substr(0, 5) == "<?xml" || head.substr(0, 9) == "<?xpacket" || head.substr(0, 10) == "<x:xmpmeta") { rc = true; } if (!advance || !rc) { iIo.seek(-len, BasicIo::cur); } return rc; }
bool isJpegType(BasicIo& iIo, bool advance) { bool result = true; byte tmpBuf[2]; iIo.read(tmpBuf, 2); if (iIo.error() || iIo.eof()) return false; if (0xff != tmpBuf[0] || JpegImage::soi_ != tmpBuf[1]) { result = false; } if (!advance || !result ) iIo.seek(-2, BasicIo::cur); return result; }
bool isMrwType(BasicIo& iIo, bool advance) { const int32_t len = 4; byte buf[len]; iIo.read(buf, len); if (iIo.error() || iIo.eof()) { return false; } int rc = memcmp(buf, "\0MRM", 4); if (!advance || rc != 0) { iIo.seek(-len, BasicIo::cur); } return rc == 0; }
bool isXmpType(BasicIo& iIo, bool advance) { /* Check if the file starts with an optional XML declaration followed by either an XMP header (<?xpacket ... ?>) or an <x:xmpmeta> element. In addition, in order for empty XmpSidecar objects as created by Exiv2 to pass the test, just an XML header is also considered ok. */ const int32_t len = 80; byte buf[len]; iIo.read(buf, xmlHdrCnt + 1); if ( iIo.eof() && 0 == strncmp(reinterpret_cast<const char*>(buf), xmlHeader, xmlHdrCnt)) { return true; } if (iIo.error() || iIo.eof()) { return false; } iIo.read(buf + xmlHdrCnt + 1, len - xmlHdrCnt - 1); if (iIo.error() || iIo.eof()) { return false; } // Skip leading BOM int32_t start = 0; if (0 == strncmp(reinterpret_cast<const char*>(buf), "\xef\xbb\xbf", 3)) { start = 3; } bool rc = false; std::string head(reinterpret_cast<const char*>(buf + start), len - start); if (head.substr(0, 5) == "<?xml") { // Forward to the next tag for (unsigned i = 5; i < head.size(); ++i) { if (head[i] == '<') { head = head.substr(i); break; } } } if ( head.size() > 9 && ( head.substr(0, 9) == "<?xpacket" || head.substr(0, 10) == "<x:xmpmeta")) { rc = true; } if (!advance || !rc) { iIo.seek(-(len - start), BasicIo::cur); // Swallow the BOM } return rc; }
bool isExvType(BasicIo& iIo, bool advance) { bool result = true; byte tmpBuf[7]; iIo.read(tmpBuf, 7); if (iIo.error() || iIo.eof()) return false; if ( 0xff != tmpBuf[0] || 0x01 != tmpBuf[1] || memcmp(tmpBuf + 2, ExvImage::exiv2Id_, 5) != 0) { result = false; } if (!advance || !result) iIo.seek(-7, BasicIo::cur); return result; }
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; }
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; }
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; }
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; }
long FileIo::write(BasicIo& src) { assert(fp_ != 0); if (static_cast<BasicIo*>(this) == &src) return 0; if (!src.isopen()) return 0; if (switchMode(opWrite) != 0) return 0; byte buf[4096]; long readCount = 0; long writeCount = 0; long writeTotal = 0; while ((readCount = src.read(buf, sizeof(buf)))) { writeTotal += writeCount = (long)std::fwrite(buf, 1, readCount, fp_); if (writeCount != readCount) { // try to reset back to where write stopped src.seek(writeCount-readCount, BasicIo::cur); break; } } return writeTotal; }
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 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