void RafImage::readMetadata() { #ifdef DEBUG std::cerr << "Reading RAF file " << io_->path() << "\n"; #endif if (io_->open() != 0) throw Error(9, io_->path(), strError()); IoCloser closer(*io_); // Ensure that this is the correct image type if (!isRafType(*io_, false)) { if (io_->error() || io_->eof()) throw Error(14); throw Error(3, "RAF"); } byte const* pData = io_->mmap(); long size = io_->size(); if (size < 88 + 4) throw Error(14); // includes the test for -1 uint32_t const start = getULong(pData + 84, bigEndian) + 12; if (static_cast<uint32_t>(size) < start) throw Error(14); clearMetadata(); ByteOrder bo = TiffParser::decode(exifData_, iptcData_, xmpData_, pData + start, size - start); exifData_["Exif.Image2.JPEGInterchangeFormat"] = getULong(pData + 84, bigEndian); exifData_["Exif.Image2.JPEGInterchangeFormatLength"] = getULong(pData + 88, bigEndian); setByteOrder(bo); } // RafImage::readMetadata
bool OrfHeader::read(const byte* pData, uint32_t size) { if (size < 8) return false; if (pData[0] == 0x49 && pData[1] == 0x49) { setByteOrder(littleEndian); } else if (pData[0] == 0x4d && pData[1] == 0x4d) { setByteOrder(bigEndian); } else { return false; } if (tag() != getUShort(pData + 2, byteOrder())) return false; setOffset(getULong(pData + 4, byteOrder())); if (offset() != 0x00000008) return false; return true; } // OrfHeader::read
bool Cr2Header::read(const byte* pData, uint32_t size) { if (size < 16) return false; if (pData[0] == 0x49 && pData[1] == 0x49) { setByteOrder(littleEndian); } else if (pData[0] == 0x4d && pData[1] == 0x4d) { setByteOrder(bigEndian); } else { return false; } if (tag() != getUShort(pData + 2, byteOrder())) return false; setOffset(getULong(pData + 4, byteOrder())); if (0 != memcmp(pData + 8, cr2sig_, 4)) return false; offset2_ = getULong(pData + 12, byteOrder()); return true; } // Cr2Header::read
void PsdImage::processResourceBlock(uint16_t resourceId, uint32_t resourceSize) { switch(resourceId) { case kPhotoshopResourceID_IPTC_NAA: { DataBuf rawIPTC(resourceSize); io_->read(rawIPTC.pData_, rawIPTC.size_); if (io_->error() || io_->eof()) throw Error(14); if (IptcParser::decode(iptcData_, rawIPTC.pData_, rawIPTC.size_)) { #ifndef SUPPRESS_WARNINGS std::cerr << "Warning: Failed to decode IPTC metadata.\n"; #endif iptcData_.clear(); } break; } case kPhotoshopResourceID_ExifInfo: { DataBuf rawExif(resourceSize); io_->read(rawExif.pData_, rawExif.size_); if (io_->error() || io_->eof()) throw Error(14); ByteOrder bo = ExifParser::decode(exifData_, rawExif.pData_, rawExif.size_); setByteOrder(bo); if (rawExif.size_ > 0 && byteOrder() == invalidByteOrder) { #ifndef SUPPRESS_WARNINGS std::cerr << "Warning: Failed to decode Exif metadata.\n"; #endif exifData_.clear(); } break; } case kPhotoshopResourceID_XMPPacket: { DataBuf xmpPacket(resourceSize); io_->read(xmpPacket.pData_, xmpPacket.size_); if (io_->error() || io_->eof()) throw Error(14); xmpPacket_.assign(reinterpret_cast<char *>(xmpPacket.pData_), xmpPacket.size_); if (xmpPacket_.size() > 0 && XmpParser::decode(xmpData_, xmpPacket_)) { #ifndef SUPPRESS_WARNINGS std::cerr << "Warning: Failed to decode XMP metadata.\n"; #endif } break; } default: { break; } } }
bool OrfHeader::read(const byte* pData, uint32_t size) { if (size < 8) return false; if (pData[0] == 0x49 && pData[1] == 0x49) { setByteOrder(littleEndian); } else if (pData[0] == 0x4d && pData[1] == 0x4d) { setByteOrder(bigEndian); } else { return false; } uint16_t sig = getUShort(pData + 2, byteOrder()); if (tag() != sig && 0x5352 != sig) return false; // #658: Added 0x5352 for SP-560UZ sig_ = sig; setOffset(getULong(pData + 4, byteOrder())); if (offset() != 0x00000008) return false; return true; } // OrfHeader::read
void MrwImage::readMetadata() { #ifdef DEBUG std::cerr << "Reading MRW file " << io_->path() << "\n"; #endif if (io_->open() != 0) { throw Error(9, io_->path(), strError()); } IoCloser closer(*io_); // Ensure that this is the correct image type if (!isMrwType(*io_, false)) { if (io_->error() || io_->eof()) throw Error(14); throw Error(3, "MRW"); } clearMetadata(); // Find the TTW block and read it into a buffer uint32_t const len = 8; byte tmp[len]; io_->read(tmp, len); uint32_t pos = len; uint32_t const end = getULong(tmp + 4, bigEndian); pos += len; if (pos > end) throw Error(14); io_->read(tmp, len); if (io_->error() || io_->eof()) throw Error(14); while (memcmp(tmp + 1, "TTW", 3) != 0) { uint32_t const siz = getULong(tmp + 4, bigEndian); pos += siz; if (pos > end) throw Error(14); io_->seek(siz, BasicIo::cur); if (io_->error() || io_->eof()) throw Error(14); pos += len; if (pos > end) throw Error(14); io_->read(tmp, len); if (io_->error() || io_->eof()) throw Error(14); } DataBuf buf(getULong(tmp + 4, bigEndian)); io_->read(buf.pData_, buf.size_); if (io_->error() || io_->eof()) throw Error(14); ByteOrder bo = TiffParser::decode(exifData_, iptcData_, xmpData_, buf.pData_, buf.size_); setByteOrder(bo); } // MrwImage::readMetadata
uint32_t PsdImage::writeExifData(const ExifData& exifData, BasicIo& out) { uint32_t resLength = 0; byte buf[8]; if (exifData.count() > 0) { Blob blob; ByteOrder bo = byteOrder(); if (bo == invalidByteOrder) { bo = littleEndian; setByteOrder(bo); } ExifParser::encode(blob, bo, exifData); if (blob.size() > 0) { #ifdef DEBUG std::cerr << std::hex << "write: resourceId: " << kPhotoshopResourceID_ExifInfo << "\n"; std::cerr << std::dec << "Writing ExifInfo: size: " << blob.size() << "\n"; #endif ul2Data(buf, kPhotoshopResourceType, bigEndian); if (out.write(buf, 4) != 4) throw Error(21); us2Data(buf, kPhotoshopResourceID_ExifInfo, bigEndian); if (out.write(buf, 2) != 2) throw Error(21); us2Data(buf, 0, bigEndian); // NULL resource name if (out.write(buf, 2) != 2) throw Error(21); ul2Data(buf, blob.size(), bigEndian); if (out.write(buf, 4) != 4) throw Error(21); // Write encoded Exif data if (out.write(&blob[0], blob.size()) != static_cast<long>(blob.size())) throw Error(21); resLength += blob.size() + 12; if (blob.size() & 1) // even padding { buf[0] = 0; if (out.write(buf, 1) != 1) throw Error(21); resLength++; } } } return resLength; } // PsdImage::writeExifData
void Jp2Image::readMetadata() { #ifdef DEBUG std::cerr << "Exiv2::Jp2Image::readMetadata: Reading JPEG-2000 file " << io_->path() << "\n"; #endif if (io_->open() != 0) { throw Error(9, io_->path(), strError()); } IoCloser closer(*io_); // Ensure that this is the correct image type if (!isJp2Type(*io_, true)) { if (io_->error() || io_->eof()) throw Error(14); throw Error(3, "JPEG-2000"); } long position = 0; Jp2BoxHeader box = {0,0}; Jp2BoxHeader subBox = {0,0}; Jp2ImageHeaderBox ihdr = {0,0,0,0,0,0,0,0}; Jp2UuidBox uuid = {{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}; while (io_->read((byte*)&box, sizeof(box)) == sizeof(box)) { position = io_->tell(); box.boxLength = getLong((byte*)&box.boxLength, bigEndian); #ifdef DEBUG std::cout << "Exiv2::Jp2Image::readMetadata: Position: " << position << "\n"; std::cout << "Exiv2::Jp2Image::readMetadata: Find box type: " << std::string((const char*)&box.boxType) << " lenght: " << box.boxLength << "\n"; #endif box.boxType = getLong((byte*)&box.boxType, bigEndian); if (box.boxLength == 0) { #ifdef DEBUG std::cout << "Exiv2::Jp2Image::readMetadata: Null Box size has been found. " "This is the last box of file.\n"; #endif return; } if (box.boxLength == 1) { // FIXME. Special case. the real box size is given in another place. } switch(box.boxType) { case kJp2BoxTypeJp2Header: { #ifdef DEBUG std::cout << "Exiv2::Jp2Image::readMetadata: JP2Header box found\n"; #endif if (io_->read((byte*)&subBox, sizeof(subBox)) == sizeof(subBox)) { subBox.boxLength = getLong((byte*)&subBox.boxLength, bigEndian); subBox.boxType = getLong((byte*)&subBox.boxType, bigEndian); if((subBox.boxType == kJp2BoxTypeImageHeader) && (io_->read((byte*)&ihdr, sizeof(ihdr)) == sizeof(ihdr))) { #ifdef DEBUG std::cout << "Exiv2::Jp2Image::readMetadata: Ihdr data found\n"; #endif ihdr.imageHeight = getLong((byte*)&ihdr.imageHeight, bigEndian); ihdr.imageWidth = getLong((byte*)&ihdr.imageWidth, bigEndian); ihdr.componentCount = getShort((byte*)&ihdr.componentCount, bigEndian); ihdr.compressionTypeProfile = getShort((byte*)&ihdr.compressionTypeProfile, bigEndian); pixelWidth_ = ihdr.imageWidth; pixelHeight_ = ihdr.imageHeight; } } break; } case kJp2BoxTypeUuid: { #ifdef DEBUG std::cout << "Exiv2::Jp2Image::readMetadata: UUID box found\n"; #endif if (io_->read((byte*)&uuid, sizeof(uuid)) == sizeof(uuid)) { DataBuf rawData; long bufRead; if(memcmp(uuid.uuid, kJp2UuidExif, sizeof(uuid)) == 0) { #ifdef DEBUG std::cout << "Exiv2::Jp2Image::readMetadata: Exif data found\n"; #endif // we've hit an embedded Exif block rawData.alloc(box.boxLength - (sizeof(box) + sizeof(uuid))); bufRead = io_->read(rawData.pData_, rawData.size_); if (io_->error()) throw Error(14); if (bufRead != rawData.size_) throw Error(20); if (rawData.size_ > 0) { // Find the position of Exif header in bytes array. const byte exifHeader[] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 }; long pos = -1; for (long i=0 ; i < rawData.size_-(long)sizeof(exifHeader) ; i++) { if (memcmp(exifHeader, &rawData.pData_[i], sizeof(exifHeader)) == 0) { pos = i; break; } } // If found it, store only these data at from this place. if (pos !=-1) { #ifdef DEBUG std::cout << "Exiv2::Jp2Image::readMetadata: Exif header found at position " << pos << "\n"; #endif pos = pos + sizeof(exifHeader); ByteOrder bo = TiffParser::decode(exifData(), iptcData(), xmpData(), rawData.pData_ + pos, rawData.size_ - pos); setByteOrder(bo); } } else { #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Failed to decode Exif metadata.\n"; #endif exifData_.clear(); } } else if(memcmp(uuid.uuid, kJp2UuidIptc, sizeof(uuid)) == 0) { // we've hit an embedded IPTC block #ifdef DEBUG std::cout << "Exiv2::Jp2Image::readMetadata: Iptc data found\n"; #endif rawData.alloc(box.boxLength - (sizeof(box) + sizeof(uuid))); bufRead = io_->read(rawData.pData_, rawData.size_); if (io_->error()) throw Error(14); if (bufRead != rawData.size_) throw Error(20); if (IptcParser::decode(iptcData_, rawData.pData_, rawData.size_)) { #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Failed to decode IPTC metadata.\n"; #endif iptcData_.clear(); } } else if(memcmp(uuid.uuid, kJp2UuidXmp, sizeof(uuid)) == 0) { // we've hit an embedded XMP block #ifdef DEBUG std::cout << "Exiv2::Jp2Image::readMetadata: Xmp data found\n"; #endif rawData.alloc(box.boxLength - (sizeof(box) + sizeof(uuid))); bufRead = io_->read(rawData.pData_, rawData.size_); if (io_->error()) throw Error(14); if (bufRead != rawData.size_) throw Error(20); xmpPacket_.assign(reinterpret_cast<char *>(rawData.pData_), rawData.size_); std::string::size_type idx = xmpPacket_.find_first_of('<'); if (idx != std::string::npos && idx > 0) { #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Removing " << static_cast<uint32_t>(idx) << " characters from the beginning of the XMP packet\n"; #endif xmpPacket_ = xmpPacket_.substr(idx); } if (xmpPacket_.size() > 0 && XmpParser::decode(xmpData_, xmpPacket_)) { #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Failed to decode XMP metadata.\n"; #endif } } } break; } default: { break; } } // Move to the next box. io_->seek(static_cast<long>(position - sizeof(box) + box.boxLength), BasicIo::beg); if (io_->error() || io_->eof()) throw Error(14); } } // Jp2Image::readMetadata
QByteArray QIconvCodec::convertFromUnicode(const QChar *uc, int len, ConverterState *convState) const { char *inBytes; char *outBytes; size_t inBytesLeft; #if defined(GNU_LIBICONV) const char **inBytesPtr = const_cast<const char **>(&inBytes); #else char **inBytesPtr = &inBytes; #endif IconvState *temporaryState = 0; QThreadStorage<QIconvCodec::IconvState *> *ts = fromUnicodeState(); IconvState *&state = (qt_locale_initialized && ts) ? ts->localData() : temporaryState; if (!state) { iconv_t cd = QIconvCodec::createIconv_t(0, UTF16); if (cd != reinterpret_cast<iconv_t>(-1)) { if (!setByteOrder(cd)) { perror("QIconvCodec::convertFromUnicode: using Latin-1 for conversion, iconv failed for BOM"); iconv_close(cd); cd = reinterpret_cast<iconv_t>(-1); return QString(uc, len).toLatin1(); } } state = new IconvState(cd); } if (state->cd == reinterpret_cast<iconv_t>(-1)) { static int reported = 0; if (!reported++) { fprintf(stderr, "QIconvCodec::convertFromUnicode: using Latin-1 for conversion, iconv_open failed\n"); } delete temporaryState; return QString(uc, len).toLatin1(); } size_t outBytesLeft = len; QByteArray ba(outBytesLeft, Qt::Uninitialized); outBytes = ba.data(); // now feed iconv() the real data inBytes = const_cast<char *>(reinterpret_cast<const char *>(uc)); inBytesLeft = len * sizeof(QChar); QByteArray in; if (convState && convState->remainingChars) { // we have one surrogate char to be prepended in.resize(sizeof(QChar) + len); inBytes = in.data(); QChar remaining = convState->state_data[0]; memcpy(in.data(), &remaining, sizeof(QChar)); memcpy(in.data() + sizeof(QChar), uc, inBytesLeft); inBytesLeft += sizeof(QChar); convState->remainingChars = 0; } int invalidCount = 0; while (inBytesLeft != 0) { if (iconv(state->cd, inBytesPtr, &inBytesLeft, &outBytes, &outBytesLeft) == (size_t) -1) { if (errno == EINVAL && convState) { // buffer ends in a surrogate Q_ASSERT(inBytesLeft == 2); convState->remainingChars = 1; convState->state_data[0] = uc[len - 1].unicode(); break; } switch (errno) { case EILSEQ: ++invalidCount; // fall through case EINVAL: { inBytes += sizeof(QChar); inBytesLeft -= sizeof(QChar); break; } case E2BIG: { int offset = ba.size() - outBytesLeft; ba.resize(ba.size() * 2); outBytes = ba.data() + offset; outBytesLeft = ba.size() - offset; break; } default: { // note, cannot use qWarning() since we are implementing the codecForLocale :) perror("QIconvCodec::convertFromUnicode: using Latin-1 for conversion, iconv failed"); // reset to initial state iconv(state->cd, 0, &inBytesLeft, 0, &outBytesLeft); delete temporaryState; return QString(uc, len).toLatin1(); } } } } // reset to initial state iconv(state->cd, 0, &inBytesLeft, 0, &outBytesLeft); setByteOrder(state->cd); ba.resize(ba.size() - outBytesLeft); if (convState) convState->invalidChars = invalidCount; delete temporaryState; return ba; }
QByteArray QIconvCodec::convertFromUnicode(const QChar *uc, int len, ConverterState *convState) const { char *inBytes; char *outBytes; size_t inBytesLeft; #if defined(GNU_LIBICONV) const char **inBytesPtr = const_cast<const char **>(&inBytes); #else char **inBytesPtr = &inBytes; #endif QThreadStorage<QIconvCodec::IconvState *> *ts = fromUnicodeState(); if (!qt_locale_initialized || !ts) { // we're running after the Q_GLOBAL_STATIC has been deleted // or before the QCoreApplication initialization // bad programmer, no cookie for you if (!len) // this is a special case - zero-sized string should be // translated to empty but not-null QByteArray. return QByteArray(""); return QString::fromRawData(uc, len).toLatin1(); } IconvState *&state = ts->localData(); if (!state) { state = new IconvState(QIconvCodec::createIconv_t(0, UTF16)); if (state->cd == reinterpret_cast<iconv_t>(-1)) { if (!setByteOrder(state->cd)) { perror("QIconvCodec::convertFromUnicode: using Latin-1 for conversion, iconv failed for BOM"); iconv_close(state->cd); state->cd = reinterpret_cast<iconv_t>(-1); return QString(uc, len).toLatin1(); } } } if (state->cd == reinterpret_cast<iconv_t>(-1)) { static int reported = 0; if (!reported++) { fprintf(stderr, "QIconvCodec::convertFromUnicode: using Latin-1 for conversion, iconv_open failed\n"); } return QString(uc, len).toLatin1(); } size_t outBytesLeft = len; QByteArray ba(outBytesLeft, Qt::Uninitialized); outBytes = ba.data(); // now feed iconv() the real data inBytes = const_cast<char *>(reinterpret_cast<const char *>(uc)); inBytesLeft = len * sizeof(QChar); QByteArray in; if (convState && convState->remainingChars) { // we have one surrogate char to be prepended in.resize(sizeof(QChar) + len); inBytes = in.data(); QChar remaining = convState->state_data[0]; memcpy(in.data(), &remaining, sizeof(QChar)); memcpy(in.data() + sizeof(QChar), uc, inBytesLeft); inBytesLeft += sizeof(QChar); convState->remainingChars = 0; } int invalidCount = 0; while (inBytesLeft != 0) { if (iconv(state->cd, inBytesPtr, &inBytesLeft, &outBytes, &outBytesLeft) == (size_t) -1) { if (errno == EINVAL && convState) { // buffer ends in a surrogate Q_ASSERT(inBytesLeft == 2); convState->remainingChars = 1; convState->state_data[0] = uc[len - 1].unicode(); break; } switch (errno) { case EILSEQ: ++invalidCount; // fall through case EINVAL: { inBytes += sizeof(QChar); inBytesLeft -= sizeof(QChar); break; } case E2BIG: { int offset = ba.size() - outBytesLeft; ba.resize(ba.size() * 2); outBytes = ba.data() + offset; outBytesLeft = ba.size() - offset; break; } default: { // note, cannot use qWarning() since we are implementing the codecForLocale :) perror("QIconvCodec::convertFromUnicode: using Latin-1 for conversion, iconv failed"); // reset to initial state iconv(state->cd, 0, &inBytesLeft, 0, &outBytesLeft); return QString(uc, len).toLatin1(); } } } } // reset to initial state iconv(state->cd, 0, &inBytesLeft, 0, &outBytesLeft); setByteOrder(state->cd); ba.resize(ba.size() - outBytesLeft); if (convState) convState->invalidChars = invalidCount; return ba; }
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 JpegBase::readMetadata() { int rc = 0; // Todo: this should be the return value if (io_->open() != 0) throw Error(9, io_->path(), strError()); IoCloser closer(*io_); // Ensure that this is the correct image type if (!isThisType(*io_, true)) { if (io_->error() || io_->eof()) throw Error(14); throw Error(15); } clearMetadata(); int search = 5; const long bufMinSize = 36; long bufRead = 0; DataBuf buf(bufMinSize); Blob iptcBlob; bool foundPsData = false; bool foundExifData = false; // Read section marker int marker = advanceToMarker(); if (marker < 0) throw Error(15); while (marker != sos_ && marker != eoi_ && search > 0) { // Read size and signature (ok if this hits EOF) std::memset(buf.pData_, 0x0, buf.size_); bufRead = io_->read(buf.pData_, bufMinSize); if (io_->error()) throw Error(14); if (bufRead < 2) throw Error(15); uint16_t size = getUShort(buf.pData_, bigEndian); if (foundPsData && marker != app13_) { // For IPTC, decrement search only after all app13 segments are // loaded, assuming they all appear in sequence. But decode IPTC // data after the loop, in case an app13 is the last segment // before sos or eoi. foundPsData = false; if (--search == 0) break; } if ( !foundExifData && marker == app1_ && memcmp(buf.pData_ + 2, exifId_, 6) == 0) { if (size < 8) { rc = 1; break; } // Seek to beginning and read the Exif data io_->seek(8 - bufRead, BasicIo::cur); DataBuf rawExif(size - 8); io_->read(rawExif.pData_, rawExif.size_); if (io_->error() || io_->eof()) throw Error(14); ByteOrder bo = ExifParser::decode(exifData_, rawExif.pData_, rawExif.size_); setByteOrder(bo); if (rawExif.size_ > 0 && byteOrder() == invalidByteOrder) { #ifndef SUPPRESS_WARNINGS std::cerr << "Warning: Failed to decode Exif metadata.\n"; #endif exifData_.clear(); } --search; foundExifData = true; } else if (marker == app1_ && memcmp(buf.pData_ + 2, xmpId_, 29) == 0) { if (size < 31) { rc = 6; break; } // Seek to beginning and read the XMP packet io_->seek(31 - bufRead, BasicIo::cur); DataBuf xmpPacket(size - 31); io_->read(xmpPacket.pData_, xmpPacket.size_); if (io_->error() || io_->eof()) throw Error(14); xmpPacket_.assign(reinterpret_cast<char*>(xmpPacket.pData_), xmpPacket.size_); if (xmpPacket_.size() > 0 && XmpParser::decode(xmpData_, xmpPacket_)) { #ifndef SUPPRESS_WARNINGS std::cerr << "Warning: Failed to decode XMP metadata.\n"; #endif } --search; } else if ( marker == app13_ && memcmp(buf.pData_ + 2, Photoshop::ps3Id_, 14) == 0) { if (size < 16) { rc = 2; break; } // Read the rest of the APP13 segment io_->seek(16 - bufRead, BasicIo::cur); DataBuf psData(size - 16); io_->read(psData.pData_, psData.size_); if (io_->error() || io_->eof()) throw Error(14); const byte *record = 0; uint32_t sizeIptc = 0; uint32_t sizeHdr = 0; #ifdef DEBUG std::cerr << "Found app13 segment, size = " << size << "\n"; //hexdump(std::cerr, psData.pData_, psData.size_); #endif // Find actual IPTC data within the APP13 segment const byte* pEnd = psData.pData_ + psData.size_; const byte* pCur = psData.pData_; while ( pCur < pEnd && 0 == Photoshop::locateIptcIrb(pCur, static_cast<long>(pEnd - pCur), &record, &sizeHdr, &sizeIptc)) { if (sizeIptc) { #ifdef DEBUG std::cerr << "Found IPTC IRB, size = " << sizeIptc << "\n"; #endif append(iptcBlob, record + sizeHdr, sizeIptc); } pCur = record + sizeHdr + sizeIptc; pCur += (sizeIptc & 1); } foundPsData = true; } else if (marker == com_ && comment_.empty()) { if (size < 2) { rc = 3; break; } // JPEGs can have multiple comments, but for now only read // the first one (most jpegs only have one anyway). Comments // are simple single byte ISO-8859-1 strings. io_->seek(2 - bufRead, BasicIo::cur); DataBuf comment(size - 2); io_->read(comment.pData_, comment.size_); if (io_->error() || io_->eof()) throw Error(14); comment_.assign(reinterpret_cast<char*>(comment.pData_), comment.size_); while ( comment_.length() && comment_.at(comment_.length()-1) == '\0') { comment_.erase(comment_.length()-1); } --search; } else if ( pixelHeight_ == 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_)) { // We hit a SOFn (start-of-frame) marker if (size < 8) { rc = 7; break; } pixelHeight_ = getUShort(buf.pData_ + 3, bigEndian); pixelWidth_ = getUShort(buf.pData_ + 5, bigEndian); if (pixelHeight_ != 0) --search; // Skip the remainder of the segment io_->seek(size-bufRead, BasicIo::cur); } else { if (size < 2) { rc = 4; break; } // Skip the remainder of the unknown segment if (io_->seek(size - bufRead, BasicIo::cur)) throw Error(14); } // Read the beginning of the next segment marker = advanceToMarker(); if (marker < 0) { rc = 5; break; } } // while there are segments to process if ( iptcBlob.size() > 0 && IptcParser::decode(iptcData_, &iptcBlob[0], static_cast<uint32_t>(iptcBlob.size()))) { #ifndef SUPPRESS_WARNINGS std::cerr << "Warning: Failed to decode IPTC metadata.\n"; #endif iptcData_.clear(); } if (rc != 0) { #ifndef SUPPRESS_WARNINGS std::cerr << "Warning: JPEG format error, rc = " << rc << "\n"; #endif } } // JpegBase::readMetadata
UTF32Encoding::UTF32Encoding(int byteOrderMark) { setByteOrder(byteOrderMark); }
UTF32Encoding::UTF32Encoding(ByteOrderType byteOrder) { setByteOrder(byteOrder); }
void PsdImage::readResourceBlock(uint16_t resourceId, uint32_t resourceSize) { switch(resourceId) { case kPhotoshopResourceID_IPTC_NAA: { DataBuf rawIPTC(resourceSize); io_->read(rawIPTC.pData_, rawIPTC.size_); if (io_->error() || io_->eof()) throw Error(14); if (IptcParser::decode(iptcData_, rawIPTC.pData_, rawIPTC.size_)) { #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Failed to decode IPTC metadata.\n"; #endif iptcData_.clear(); } break; } case kPhotoshopResourceID_ExifInfo: { DataBuf rawExif(resourceSize); io_->read(rawExif.pData_, rawExif.size_); if (io_->error() || io_->eof()) throw Error(14); ByteOrder bo = ExifParser::decode(exifData_, rawExif.pData_, rawExif.size_); setByteOrder(bo); if (rawExif.size_ > 0 && byteOrder() == invalidByteOrder) { #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Failed to decode Exif metadata.\n"; #endif exifData_.clear(); } break; } case kPhotoshopResourceID_XMPPacket: { DataBuf xmpPacket(resourceSize); io_->read(xmpPacket.pData_, xmpPacket.size_); if (io_->error() || io_->eof()) throw Error(14); xmpPacket_.assign(reinterpret_cast<char *>(xmpPacket.pData_), xmpPacket.size_); if (xmpPacket_.size() > 0 && XmpParser::decode(xmpData_, xmpPacket_)) { #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Failed to decode XMP metadata.\n"; #endif } break; } // - PS 4.0 preview data is fetched from ThumbnailResource // - PS >= 5.0 preview data is fetched from ThumbnailResource2 case kPhotoshopResourceID_ThumbnailResource: case kPhotoshopResourceID_ThumbnailResource2: { /* Photoshop thumbnail resource header offset length name description ====== ======== ==== =========== 0 4 bytes format = 1 (kJpegRGB). Also supports kRawRGB (0). 4 4 bytes width Width of thumbnail in pixels. 8 4 bytes height Height of thumbnail in pixels. 12 4 bytes widthbytes Padded row bytes as (width * bitspixel + 31) / 32 * 4. 16 4 bytes size Total size as widthbytes * height * planes 20 4 bytes compressedsize Size after compression. Used for consistentcy check. 24 2 bytes bitspixel = 24. Bits per pixel. 26 2 bytes planes = 1. Number of planes. 28 variable data JFIF data in RGB format. Note: For resource ID 1033 the data is in BGR format. */ byte buf[28]; if (io_->read(buf, 28) != 28) { throw Error(3, "Photoshop"); } NativePreview nativePreview; nativePreview.position_ = io_->tell(); nativePreview.size_ = getLong(buf + 20, bigEndian); // compressedsize nativePreview.width_ = getLong(buf + 4, bigEndian); nativePreview.height_ = getLong(buf + 8, bigEndian); const uint32_t format = getLong(buf + 0, bigEndian); if (nativePreview.size_ > 0 && nativePreview.position_ >= 0) { io_->seek(static_cast<long>(nativePreview.size_), BasicIo::cur); if (io_->error() || io_->eof()) throw Error(14); if (format == 1) { nativePreview.filter_ = ""; nativePreview.mimeType_ = "image/jpeg"; nativePreviews_.push_back(nativePreview); } else { // unsupported format of native preview } } break; } default: { break; } } } // PsdImage::readResourceBlock