DataBuf IptcParser::encode(const IptcData& iptcData) { DataBuf buf(iptcData.size()); byte *pWrite = buf.pData_; IptcData::const_iterator iter = iptcData.begin(); IptcData::const_iterator end = iptcData.end(); for ( ; iter != end; ++iter) { // marker, record Id, dataset num *pWrite++ = marker_; *pWrite++ = static_cast<byte>(iter->record()); *pWrite++ = static_cast<byte>(iter->tag()); // extended or standard dataset? long dataSize = iter->size(); if (dataSize > 32767) { // always use 4 bytes for extended length uint16_t sizeOfSize = 4 | 0x8000; us2Data(pWrite, sizeOfSize, bigEndian); pWrite += 2; ul2Data(pWrite, dataSize, bigEndian); pWrite += 4; } else { us2Data(pWrite, static_cast<uint16_t>(dataSize), bigEndian); pWrite += 2; } pWrite += iter->value().copy(pWrite, bigEndian); } return buf; } // IptcParser::encode
uint32_t PsdImage::writeIptcData(const IptcData& iptcData, BasicIo& out) const { uint32_t resLength = 0; byte buf[8]; if (iptcData.count() > 0) { DataBuf rawIptc = IptcParser::encode(iptcData); if (rawIptc.size_ > 0) { #ifdef DEBUG std::cerr << std::hex << "write: resourceId: " << kPhotoshopResourceID_IPTC_NAA << "\n"; std::cerr << std::dec << "Writing IPTC_NAA: size: " << rawIptc.size_ << "\n"; #endif ul2Data(buf, kPhotoshopResourceType, bigEndian); if (out.write(buf, 4) != 4) throw Error(21); us2Data(buf, kPhotoshopResourceID_IPTC_NAA, 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, rawIptc.size_, bigEndian); if (out.write(buf, 4) != 4) throw Error(21); // Write encoded Iptc data if (out.write(rawIptc.pData_, rawIptc.size_) != rawIptc.size_) throw Error(21); resLength += rawIptc.size_ + 12; if (rawIptc.size_ & 1) // even padding { buf[0] = 0; if (out.write(buf, 1) != 1) throw Error(21); resLength++; } } } return resLength; } // PsdImage::writeIptcData
void processAdd(const std::string& line, int num) { std::string::size_type keyStart = line.find_first_not_of(" \t", 1); std::string::size_type keyEnd = line.find_first_of(" \t", keyStart+1); std::string::size_type dataStart = line.find_first_not_of(" \t", keyEnd+1); if (keyStart == std::string::npos || keyEnd == std::string::npos || dataStart == std::string::npos) { std::ostringstream os; os << "Invalid \'a\' command at line " << num; throw Error(os.str()); } std::string key(line.substr(keyStart, keyEnd-keyStart)); std::pair<uint16, uint16> p = IptcDataSets::decomposeKey(key); if (p.first == 0xffff) throw Error("Invalid key " + key); if (p.second == IptcDataSets::invalidRecord) throw Error("Invalid key " + key); std::string data(line.substr(dataStart)); // if data starts and ends with quotes, remove them if (data.at(0) == '\"' && data.at(data.size()-1) == '\"') { data = data.substr(1, data.size()-2); } TypeId type = IptcDataSets::dataSetType(p.first, p.second); Value *val = Value::create(type); val->read(data); int rc = g_iptcData.add(IptcKey(key), val); if (rc) { std::string error = IptcData::strError(rc, "Input file"); throw Error(error); } }
void processAdd(const std::string& line, int num, IptcData &iptcData) { std::string::size_type keyStart = line.find_first_not_of(" \t", 1); std::string::size_type keyEnd = line.find_first_of(" \t", keyStart+1); std::string::size_type dataStart = line.find_first_not_of(" \t", keyEnd+1); if (keyStart == std::string::npos || keyEnd == std::string::npos || dataStart == std::string::npos) { std::ostringstream os; os << "Invalid \'a\' command at line " << num; throw Error(1, os.str()); } std::string key(line.substr(keyStart, keyEnd-keyStart)); IptcKey iptcKey(key); std::string data(line.substr(dataStart)); // if data starts and ends with quotes, remove them if (data.at(0) == '\"' && data.at(data.size()-1) == '\"') { data = data.substr(1, data.size()-2); } TypeId type = IptcDataSets::dataSetType(iptcKey.tag(), iptcKey.record()); Value::AutoPtr value = Value::create(type); value->read(data); int rc = iptcData.add(iptcKey, value.get()); if (rc) { throw Error(1, "Iptc dataset already exists and is not repeatable"); } }
void processRemove(const std::string& line, int num, IptcData &iptcData) { std::string::size_type keyStart = line.find_first_not_of(" \t", 1); if (keyStart == std::string::npos) { std::ostringstream os; os << "Invalid \'r\' command at line " << num; throw Error(1, os.str()); } const std::string key( line.substr(keyStart) ); IptcKey iptcKey(key); IptcData::iterator iter = iptcData.findKey(iptcKey); if (iter != iptcData.end()) { iptcData.erase(iter); } }
DataBuf Photoshop::setIptcIrb(const byte* pPsData, long sizePsData, const IptcData& iptcData) { if (sizePsData > 0) assert(pPsData); #ifdef DEBUG std::cerr << "IRB block at the beginning of Photoshop::setIptcIrb\n"; if (sizePsData == 0) std::cerr << " None.\n"; else hexdump(std::cerr, pPsData, sizePsData); #endif const byte* record = pPsData; uint32_t sizeIptc = 0; uint32_t sizeHdr = 0; DataBuf rc; // Safe to call with zero psData.size_ if (0 > Photoshop::locateIptcIrb(pPsData, sizePsData, &record, &sizeHdr, &sizeIptc)) { return rc; } Blob psBlob; const uint32_t sizeFront = static_cast<uint32_t>(record - pPsData); // Write data before old record. if (sizePsData > 0 && sizeFront > 0) { append(psBlob, pPsData, sizeFront); } // Write new iptc record if we have it DataBuf rawIptc(iptcData.copy()); if (rawIptc.size_ > 0) { byte tmpBuf[12]; memcpy(tmpBuf, Photoshop::bimId_, 4); us2Data(tmpBuf + 4, iptc_, bigEndian); tmpBuf[6] = 0; tmpBuf[7] = 0; ul2Data(tmpBuf + 8, rawIptc.size_, bigEndian); append(psBlob, tmpBuf, 12); append(psBlob, rawIptc.pData_, rawIptc.size_); // Data is padded to be even (but not included in size) if (rawIptc.size_ & 1) psBlob.push_back(0x00); } // Write existing stuff after record, data is rounded to be even. const uint32_t sizeOldData = sizeHdr + sizeIptc + (sizeIptc & 1); // Note: Because of the rounding, sizeFront + sizeOldData can be // _greater_ than sizePsData by 1 (not just equal), if the original // data was not padded. if (static_cast<uint32_t>(sizePsData) > sizeFront + sizeOldData) { append(psBlob, record + sizeOldData, sizePsData - sizeFront - sizeOldData); } if (psBlob.size() > 0) rc = DataBuf(&psBlob[0], static_cast<long>(psBlob.size())); #ifdef DEBUG std::cerr << "IRB block at the end of Photoshop::setIptcIrb\n"; if (rc.size_ == 0) std::cerr << " None.\n"; else hexdump(std::cerr, rc.pData_, rc.size_); #endif return rc; } // Photoshop::setIptcIrb
void processRemove(const std::string& line, int num) { std::string::size_type keyStart = line.find_first_not_of(" \t", 1); if (keyStart == std::string::npos) { std::ostringstream os; os << "Invalid \'r\' command at line " << num; throw Error(os.str()); } const std::string key( line.substr(keyStart) ); std::pair<uint16, uint16> p = IptcDataSets::decomposeKey(key); if (p.first == 0xffff) throw Error("Invalid key" + key); if (p.second == IptcDataSets::invalidRecord) throw Error("Invalid key" + key); IptcData::iterator iter = g_iptcData.findId(p.first, p.second); if (iter != g_iptcData.end()) { g_iptcData.erase(iter); } }
ByteOrder ExifParser::decode( ExifData& exifData, const byte* pData, uint32_t size ) { IptcData iptcData; XmpData xmpData; ByteOrder bo = TiffParser::decode(exifData, iptcData, xmpData, pData, size); #ifndef SUPPRESS_WARNINGS if (!iptcData.empty()) { EXV_WARNING << "Ignoring IPTC information encoded in the Exif data.\n"; } if (!xmpData.empty()) { EXV_WARNING << "Ignoring XMP information encoded in the Exif data.\n"; } #endif return bo; } // ExifParser::decode
DataBuf IptcParser::encode(const IptcData& iptcData) { DataBuf buf(iptcData.size()); byte *pWrite = buf.pData_; // Copy the iptc data sets and sort them by record but preserve the order of datasets IptcMetadata sortedIptcData; std::copy(iptcData.begin(), iptcData.end(), std::back_inserter(sortedIptcData)); std::stable_sort(sortedIptcData.begin(), sortedIptcData.end(), cmpIptcdataByRecord); IptcData::const_iterator iter = sortedIptcData.begin(); IptcData::const_iterator end = sortedIptcData.end(); for ( ; iter != end; ++iter) { // marker, record Id, dataset num *pWrite++ = marker_; *pWrite++ = static_cast<byte>(iter->record()); *pWrite++ = static_cast<byte>(iter->tag()); // extended or standard dataset? long dataSize = iter->size(); if (dataSize > 32767) { // always use 4 bytes for extended length uint16_t sizeOfSize = 4 | 0x8000; us2Data(pWrite, sizeOfSize, bigEndian); pWrite += 2; ul2Data(pWrite, dataSize, bigEndian); pWrite += 4; } else { us2Data(pWrite, static_cast<uint16_t>(dataSize), bigEndian); pWrite += 2; } pWrite += iter->value().copy(pWrite, bigEndian); } return buf; } // IptcParser::encode
// ***************************************************************************** // Main int main(int argc, char* const argv[]) { try { if (argc != 2) { std::cout << "Usage: " << argv[0] << " image\n"; std::cout << "Commands read from stdin.\n"; return 1; } int rc = g_iptcData.read(argv[1]); if (rc) { std::string error = IptcData::strError(rc, argv[1]); throw Error(error); } std::string line; int num = 0; std::getline(std::cin, line); while (line.length() && processLine(line, ++num)) { std::getline(std::cin, line); } rc = g_iptcData.write(argv[1]); if (rc) { std::string error = IptcData::strError(rc, argv[1]); throw Error(error); } return rc; } catch (Error& e) { std::cout << "Caught Exiv2 exception '" << e << "'\n"; return -1; } }
int IptcParser::decode( IptcData& iptcData, const byte* pData, uint32_t size ) { #ifdef DEBUG std::cerr << "IptcParser::decode, size = " << size << "\n"; #endif const byte* pRead = pData; iptcData.clear(); uint16_t record = 0; uint16_t dataSet = 0; uint32_t sizeData = 0; byte extTest = 0; while (pRead + 3 < pData + size) { // First byte should be a marker. If it isn't, scan forward and skip // the chunk bytes present in some images. This deviates from the // standard, which advises to treat such cases as errors. if (*pRead++ != marker_) continue; record = *pRead++; dataSet = *pRead++; extTest = *pRead; if (extTest & 0x80) { // extended dataset uint16_t sizeOfSize = (getUShort(pRead, bigEndian) & 0x7FFF); if (sizeOfSize > 4) return 5; pRead += 2; sizeData = 0; for (; sizeOfSize > 0; --sizeOfSize) { sizeData |= *pRead++ << (8 *(sizeOfSize-1)); } } else { // standard dataset sizeData = getUShort(pRead, bigEndian); pRead += 2; } if (pRead + sizeData <= pData + size) { int rc = 0; if ((rc = readData(iptcData, dataSet, record, pRead, sizeData)) != 0) { #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Failed to read IPTC dataset " << IptcKey(dataSet, record) << " (rc = " << rc << "); skipped.\n"; #endif } } #ifndef SUPPRESS_WARNINGS else { EXV_WARNING << "IPTC dataset " << IptcKey(dataSet, record) << " has invalid size " << sizeData << "; skipped.\n"; } #endif pRead += sizeData; } return 0; } // IptcParser::decode