void CrwImage::readMetadata() { #ifdef DEBUG std::cerr << "Reading CRW 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 (!isThisType(*io_, false)) { if (io_->error() || io_->eof()) throw Error(14); throw Error(33); } clearMetadata(); // Read the image into a memory buffer long imageSize = io_->size(); DataBuf image(imageSize); io_->read(image.pData_, imageSize); if (io_->error() || io_->eof()) throw Error(14); // Parse the image RawMetadata::AutoPtr parseTree(new CiffHeader); parseTree->read(image.pData_, image.size_, 0, invalidByteOrder); #ifdef DEBUG parseTree->print(std::cerr, invalidByteOrder); #endif parseTree->extract(*this, invalidByteOrder); } // CrwImage::readMetadata
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 (!isThisType(*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); TiffParser::decode(this, buf.pData_, buf.size_, TiffCreator::create); } // MrwImage::readMetadata
void TiffImage::readMetadata() { #ifdef DEBUG std::cerr << "Reading TIFF 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 (!isThisType(*io_, false)) { if (io_->error() || io_->eof()) throw Error(14); throw Error(33); } clearMetadata(); // Read the image into a memory buffer long len = io_->size(); DataBuf buf(len); io_->read(buf.pData_, len); if (io_->error() || io_->eof()) throw Error(14); TiffParser::decode(this, buf.pData_, buf.size_, TiffCreator::create); } // TiffImage::readMetadata
bool CrwImage::good() const { if (io_->open() != 0) return false; IoCloser closer(*io_); return isThisType(*io_, false); }
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
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 = 16; long bufRead = 0; DataBuf buf(bufMinSize); const long seek = io_->tell(); int count = 0; int search = 0; int insertPos = 0; int skipApp1Exif = -1; int skipApp13Ps3 = -1; int skipCom = -1; DataBuf psData; // 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 < 3) { // 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 (marker == app1_ && memcmp(buf.pData_ + 2, exifId_, 6) == 0) { if (size < 8) throw Error(22); skipApp1Exif = count; ++search; if (io_->seek(size-bufRead, BasicIo::cur)) throw Error(22); } else if (marker == app13_ && memcmp(buf.pData_ + 2, Photoshop::ps3Id_, 14) == 0) { if (size < 16) throw Error(22); skipApp13Ps3 = count; ++search; // needed if bufMinSize!=16: io_->seek(16-bufRead, BasicIo::cur); psData.alloc(size - 16); // Load PS data now to allow reinsertion at any point io_->read(psData.pData_, psData.size_); 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); } marker = advanceToMarker(); if (marker < 0) throw Error(22); ++count; } if (exifData_.count() > 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[18]; if (!comment_.empty()) { // 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; } if (exifData_.count() > 0) { DataBuf rawExif = exifData_.copy(); if (rawExif.size_ > 0) { // Write APP1 marker, size of APP1 field, Exif id and Exif data tmpBuf[0] = 0xff; tmpBuf[1] = app1_; if (rawExif.size_ + 8 > 0xffff) throw Error(37, "Exif"); us2Data(tmpBuf + 2, static_cast<uint16_t>(rawExif.size_ + 8), bigEndian); memcpy(tmpBuf + 4, exifId_, 6); if (outIo.write(tmpBuf, 10) != 10) throw Error(21); // Write new Exif data buffer if ( outIo.write(rawExif.pData_, rawExif.size_) != rawExif.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); 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 (marker == eoi_) { break; } else if (skipApp1Exif==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 = 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