int set_date(const ExifData& exif, char date[]) { const ExifData::const_iterator end = exif.end(); ExifData::const_iterator value = exif.findKey(ExifDateTimeCreated); if(value == end) value = exif.findKey(ExifDateTimeDigitized); if(value == end) return 1; const std::string dateTime = value->value().toString(); if(!dateTime.empty()) { //*date = (char*)realloc(*date, (dateTime.length() + 1) * sizeof(char)); strcpy(date, const_cast<char*>(dateTime.c_str())); // format gets returned as 'YYYY:MM:DD HH:MM:SS' when it // needs to be 'YYYY-MM-DDTHH:MM:SS' so fix separators char* dateStr = date; if(dateTime.length() > 4 && dateStr[4] != '-') dateStr[4] = '-'; if(dateTime.length() > 6 && dateStr[7] != '-') dateStr[7] = '-'; if(dateTime.length() > 10 && dateStr[10] != 'T') dateStr[10] = 'T'; } return 0; }
int set_long_value(const ExifData& exif, const ExifKey& key, unsigned int& field) { const ExifData::const_iterator end = exif.end(); const ExifData::const_iterator value = exif.findKey(key); if(value == end) return -1; field = value->value().toLong(); return 0; }
WriteMethod ExifParser::encode( Blob& blob, const byte* pData, uint32_t size, ByteOrder byteOrder, const ExifData& exifData ) { ExifData ed = exifData; // Delete IFD0 tags that are "not recorded" in compressed images // Reference: Exif 2.2 specs, 4.6.8 Tag Support Levels, section A static const char* filteredIfd0Tags[] = { "Exif.Image.PhotometricInterpretation", "Exif.Image.StripOffsets", "Exif.Image.RowsPerStrip", "Exif.Image.StripByteCounts", "Exif.Image.JPEGInterchangeFormat", "Exif.Image.JPEGInterchangeFormatLength", "Exif.Image.SubIFDs" }; for (unsigned int i = 0; i < EXV_COUNTOF(filteredIfd0Tags); ++i) { ExifData::iterator pos = ed.findKey(ExifKey(filteredIfd0Tags[i])); if (pos != ed.end()) { #ifdef DEBUG std::cerr << "Warning: Exif tag " << pos->key() << " not encoded\n"; #endif ed.erase(pos); } } // Delete IFDs which do not occur in JPEGs static const IfdId filteredIfds[] = { subImage1Id, subImage2Id, subImage3Id, subImage4Id, subImage5Id, subImage6Id, subImage7Id, subImage8Id, subImage9Id, subThumb1Id, panaRawId, ifd2Id, ifd3Id }; for (unsigned int i = 0; i < EXV_COUNTOF(filteredIfds); ++i) { #ifdef DEBUG std::cerr << "Warning: Exif IFD " << filteredIfds[i] << " not encoded\n"; #endif eraseIfd(ed, filteredIfds[i]); } // IPTC and XMP are stored elsewhere, not in the Exif APP1 segment. IptcData emptyIptc; XmpData emptyXmp; // Encode and check if the result fits into a JPEG Exif APP1 segment MemIo mio1; std::auto_ptr<TiffHeaderBase> header(new TiffHeader(byteOrder, 0x00000008, false)); WriteMethod wm = TiffParserWorker::encode(mio1, pData, size, ed, emptyIptc, emptyXmp, Tag::root, TiffMapping::findEncoder, header.get(), 0); if (mio1.size() <= 65527) { append(blob, mio1.mmap(), mio1.size()); return wm; } // If it doesn't fit, remove additional tags // Delete preview tags if the preview is larger than 32kB. // Todo: Enhance preview classes to be able to write and delete previews and use that instead. // Table must be sorted by preview, the first tag in each group is the size static const PreviewTags filteredPvTags[] = { { pttLen, "Exif.Minolta.ThumbnailLength" }, { pttTag, "Exif.Minolta.ThumbnailOffset" }, { pttLen, "Exif.Minolta.Thumbnail" }, { pttLen, "Exif.NikonPreview.JPEGInterchangeFormatLength" }, { pttIfd, "NikonPreview" }, { pttLen, "Exif.Olympus.ThumbnailLength" }, { pttTag, "Exif.Olympus.ThumbnailOffset" }, { pttLen, "Exif.Olympus.ThumbnailImage" }, { pttLen, "Exif.Olympus.Thumbnail" }, { pttLen, "Exif.Olympus2.ThumbnailLength" }, { pttTag, "Exif.Olympus2.ThumbnailOffset" }, { pttLen, "Exif.Olympus2.ThumbnailImage" }, { pttLen, "Exif.Olympus2.Thumbnail" }, { pttLen, "Exif.OlympusCs.PreviewImageLength" }, { pttTag, "Exif.OlympusCs.PreviewImageStart" }, { pttTag, "Exif.OlympusCs.PreviewImageValid" }, { pttLen, "Exif.Pentax.PreviewLength" }, { pttTag, "Exif.Pentax.PreviewOffset" }, { pttTag, "Exif.Pentax.PreviewResolution" }, { pttLen, "Exif.PentaxDng.PreviewLength" }, { pttTag, "Exif.PentaxDng.PreviewOffset" }, { pttTag, "Exif.PentaxDng.PreviewResolution" }, { pttLen, "Exif.SamsungPreview.JPEGInterchangeFormatLength" }, { pttIfd, "SamsungPreview" }, { pttLen, "Exif.Thumbnail.StripByteCounts" }, { pttIfd, "Thumbnail" }, { pttLen, "Exif.Thumbnail.JPEGInterchangeFormatLength" }, { pttIfd, "Thumbnail" } }; bool delTags = false; ExifData::iterator pos; for (unsigned int i = 0; i < EXV_COUNTOF(filteredPvTags); ++i) { switch (filteredPvTags[i].ptt_) { case pttLen: delTags = false; pos = ed.findKey(ExifKey(filteredPvTags[i].key_)); if (pos != ed.end() && sumToLong(*pos) > 32768) { delTags = true; #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Exif tag " << pos->key() << " not encoded\n"; #endif ed.erase(pos); } break; case pttTag: if (delTags) { pos = ed.findKey(ExifKey(filteredPvTags[i].key_)); if (pos != ed.end()) { #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Exif tag " << pos->key() << " not encoded\n"; #endif ed.erase(pos); } } break; case pttIfd: if (delTags) { #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Exif IFD " << filteredPvTags[i].key_ << " not encoded\n"; #endif eraseIfd(ed, Internal::groupId(filteredPvTags[i].key_)); } break; } } // Delete unknown tags larger than 4kB and known tags larger than 40kB. for (ExifData::iterator pos = ed.begin(); pos != ed.end(); ) { if ( (pos->size() > 4096 && pos->tagName().substr(0, 2) == "0x") || pos->size() > 40960) { #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Exif tag " << pos->key() << " not encoded\n"; #endif pos = ed.erase(pos); } else { ++pos; } } // Encode the remaining Exif tags again, don't care if it fits this time MemIo mio2; wm = TiffParserWorker::encode(mio2, pData, size, ed, emptyIptc, emptyXmp, Tag::root, TiffMapping::findEncoder, header.get(), 0); append(blob, mio2.mmap(), mio2.size()); #ifdef DEBUG if (wm == wmIntrusive) { std::cerr << "SIZE OF EXIF DATA IS " << std::dec << mio2.size() << " BYTES\n"; } else { std::cerr << "SIZE DOESN'T MATTER, NON-INTRUSIVE WRITING USED\n"; } #endif return wm; } // ExifParser::encode
void Exiv2Lib::setWhiteBalanceCoeffsCanon(ExifData& data, float wb[3]) { ExifData::const_iterator pos; if (*mExifInfo.model == "Canon EOS 300D DIGITAL" || *mExifInfo.model == "Canon EOS DIGITAL REBEL") { pos = data.findKey(ExifKey("Exif.Canon.WhiteBalanceTable")); if (pos != data.end()) { qDebug() << "Reading EOS 300D tags"; uint16_t* cdata = new uint16_t[pos->size() / 2]; pos->copy((unsigned char*)cdata, littleEndian); wb[0] = cdata[1]; wb[1] = (cdata[2] + cdata[3]) / 2; wb[2] = cdata[4]; float mx = max3(wb[0], wb[1], wb[2]); wb[0] /= mx; wb[1] /= mx; wb[2] /= mx; delete [] cdata; return; } } pos = data.findKey(ExifKey("Exif.Canon.ColorData")); if (pos != data.end()) { uint8_t* cdata = new uint8_t[pos->size()]; struct Canon_ColorData* colorData; pos->copy(cdata, littleEndian); colorData = (struct Canon_ColorData*)cdata; if (*mExifInfo.model == "Canon EOS 350D DIGITAL") // || EOS 20D { // optimize with bitshifts later // wb[0] = colorData->V1.WhiteBalanceTable[WB_AsShot].RGGB[2]; // wb[1] = ((colorData->V1.WhiteBalanceTable[WB_AsShot].RGGB[1]+colorData->V1.WhiteBalanceTable[WB_AsShot].RGGB[3])/2); // wb[2] = colorData->V1.WhiteBalanceTable[WB_AsShot].RGGB[0]; wb[0] = colorData->V1.WhiteBalanceTable[WB_AsShot].RGGB[0]; wb[1] = ((colorData->V1.WhiteBalanceTable[WB_AsShot].RGGB[1] + colorData->V1.WhiteBalanceTable[WB_AsShot].RGGB[2]) / 2); wb[2] = colorData->V1.WhiteBalanceTable[WB_AsShot].RGGB[3]; qDebug() << "WB EOS 350D DIGITAL RGGB" << wb[0] << "," << wb[1] << "," << wb[2]; } else if (*mExifInfo.model == "Canon EOS 1D Mark II") // || 1Ds Mark II { qDebug() << "Whitebalance info unavailable"; } else if (*mExifInfo.model == "Canon G10") { qDebug() << "Whitebalance info unavailable"; } else if (*mExifInfo.model == "Canon PowerShot S30") { wb[0] = colorData->V5.WhiteBalanceTable[WB_AsShot].GRBG[1]; wb[1] = (colorData->V5.WhiteBalanceTable[WB_AsShot].GRBG[0] + colorData->V5.WhiteBalanceTable[WB_AsShot].GRBG[3]) / 2; wb[2] = colorData->V5.WhiteBalanceTable[WB_AsShot].GRBG[2]; } else if (*mExifInfo.model == "Canon PowerShot S110") { wb[0] = colorData->V3.WhiteBalanceTable[WB_AsShot].RGGB[0]; wb[1] = (colorData->V3.WhiteBalanceTable[WB_AsShot].RGGB[1] + colorData->V3.WhiteBalanceTable[WB_AsShot].RGGB[2]) / 2; wb[2] = colorData->V3.WhiteBalanceTable[WB_AsShot].RGGB[3]; } else // attempt to read at the default position at V4 { qDebug() << "Model unknown, Parsing whitebalance using canon default format"; qDebug() << "Color data version: " << colorData->V7.version; // optimize with bitshifs later wb[0] = colorData->V7.WhiteBalanceTable[WB_AsShot].RGGB[0]; wb[1] = (colorData->V7.WhiteBalanceTable[WB_AsShot].RGGB[1] + colorData->V7.WhiteBalanceTable[WB_AsShot].RGGB[2]) / 2; wb[2] = colorData->V7.WhiteBalanceTable[WB_AsShot].RGGB[3]; } float mx = max3(wb[0], wb[1], wb[2]); wb[0] /= mx; wb[1] /= mx; wb[2] /= mx; delete [] cdata; } else { //qDebug() << "no such exif key"; wb[0] = 1.0; wb[1] = 1.0; wb[2] = 1.0; } // qDebug() << "WB RGGB Multipliers" <<wb[0] <<","<<wb[1]<<","<<wb[2]; }
void Exiv2Lib::setWhiteBalanceCoeffsNikon(ExifData& data, float wb[3]) { ExifData::const_iterator pos; /* * This seems to be unnecessary as the multipliers are also * available as rational numbers in the WB_RBLevels tag */ /* pos = data.findKey(ExifKey("Exif.Nikon3.Version")); int makerNoteVersion = -1; if (pos != data.end()) { char* buf = new char[pos->size()]; pos->copy((unsigned char*)buf, littleEndian); QString val(buf); qDebug() << val; delete [] buf; makerNoteVersion = val.toInt(); } qDebug() << "Nikon makenote version:" << makerNoteVersion; if (makerNoteVersion >= 200) { qDebug() << "Decrypt Nikon Color Balance"; } */ pos = data.findKey(ExifKey("Exif.Nikon3.WB_RBLevels")); if (pos != data.end()) { if (*mExifInfo.model == "NIKON D300") { wb[0] = 1.0f; wb[1] = 1.0f; wb[2] = 1.0f; // wb[0] = colorData->V3.WhiteBalanceTable[WB_AsShot].RGGB[0]; // wb[1] = // ((colorData->V1.WhiteBalanceTable[WB_AsShot].RGGB[1] + // colorData->V1.WhiteBalanceTable[WB_AsShot].RGGB[2]) / 2); // wb[2] = colorData->V1.WhiteBalanceTable[WB_AsShot].RGGB[3]; qDebug() << "White balance for model Nikon D300"; if (pos != data.end()) { wb[0] = pos->toFloat(0); wb[1] = 1.0f; wb[2] = pos->toFloat(1); qDebug() << "Nikon: RGB" << wb[0] << "," << wb[1] << "," << wb[2]; } } float mx = max3(wb[0], wb[1], wb[2]); wb[0] /= mx; wb[1] /= mx; wb[2] /= mx; } else { qDebug () << "Nikon: Color balance data not available"; wb[0] = 1.0f; wb[1] = 1.0f; wb[2] = 1.0f; } }