// @mfunc Attempt to read the number of elements given by // <p elementCount> and described by <p elementType> and // <p externalElementSize> from the data stream into the buffer // at address <p elements>. The actual number of elements read // is returned in <p elementsRead>. // @parm The element type // @parm The external element size // @parm The address of the buffer into which the elements should be read. // @parm The number of elements to read. // @parm The actual number of elements that were read. // @this const void OMDataStreamProperty::readTypedElements(const OMType* elementType, OMUInt32 externalElementSize, OMByte* elements, OMUInt32 elementCount, OMUInt32& elementsRead) const { TRACE("OMDataStreamProperty::readTypedElements"); PRECONDITION("Optional property is present", IMPLIES(isOptional(), isPresent())); PRECONDITION("Valid element type", elementType != 0); PRECONDITION("Valid element size", externalElementSize!= 0); PRECONDITION("Valid buffer", elements != 0); PRECONDITION("Valid element count", elementCount > 0); PRECONDITION("Stream byte order is known", hasByteOrder()); OMUInt64 currentPosition = position(); OMUInt64 streamSize = size(); OMUInt32 readCount = 0; if (currentPosition < streamSize) { OMUInt64 remaining = (streamSize - currentPosition) / externalElementSize; if (remaining < elementCount) { readCount = static_cast<OMUInt32>(remaining); } else { readCount = elementCount; } } if (readCount > 0) { bool reorder = false; if (byteOrder() != hostByteOrder()) { reorder = true; } // Allocate buffer for one element OMByte* buffer = new OMByte[externalElementSize]; for (OMUInt32 i = 0; i < readCount; i++) { // Read an element of the property value OMUInt32 actualByteCount; read(buffer, externalElementSize, actualByteCount); ASSERT("All bytes read", actualByteCount == externalElementSize); // Reorder an element of the property value if (reorder) { elementType->reorder(buffer, externalElementSize); } // Internalize an element of the property value OMUInt32 requiredBytesSize = elementType->internalSize( buffer, externalElementSize); elementType->internalize(buffer, externalElementSize, &elements[i * requiredBytesSize], requiredBytesSize, hostByteOrder()); } delete [] buffer; } elementsRead = readCount; }
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
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