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 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 = 3; const long bufMinSize = 16; long bufRead = 0; DataBuf buf(bufMinSize); // 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) bufRead = io_->read(buf.pData_, bufMinSize); if (io_->error()) throw Error(14); uint16_t size = getUShort(buf.pData_, bigEndian); if (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); long sizeExifData = size - 8; DataBuf rawExif(sizeExifData); io_->read(rawExif.pData_, sizeExifData); if (io_->error() || io_->eof()) throw Error(14); if (exifData_.load(rawExif.pData_, sizeExifData)) { #ifndef SUPPRESS_WARNINGS std::cerr << "Warning: Failed to decode Exif metadata.\n"; #endif exifData_.clear(); } --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 // needed if bufMinSize!=16: 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; // Find actual Iptc data within the APP13 segment if (!Photoshop::locateIptcIrb(psData.pData_, psData.size_, &record, &sizeHdr, &sizeIptc)) { if (sizeIptc) { if (iptcData_.load(record + sizeHdr, sizeIptc)) { #ifndef SUPPRESS_WARNINGS std::cerr << "Warning: Failed to decode IPTC metadata.\n"; #endif iptcData_.clear(); } } } --search; } 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); buf.alloc(size-2); io_->read(buf.pData_, size-2); if (io_->error() || io_->eof()) throw Error(14); comment_.assign(reinterpret_cast<char*>(buf.pData_), size-2); while ( comment_.length() && comment_.at(comment_.length()-1) == '\0') { comment_.erase(comment_.length()-1); } --search; } 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 (rc != 0) { #ifndef SUPPRESS_WARNINGS std::cerr << "Warning: JPEG format error, rc = " << rc << "\n"; #endif } } // JpegBase::readMetadata