Exemple #1
0
// Always creates a new metadata entry.
// Passing invalidTypeId causes the type to be guessed.
// Guessing types is accurate for IPTC, but not for EXIF.
// Returns 0 on success
EXIVSIMPLE_API int AddMeta(HIMAGE img, const char *key, const char *val, DllTypeId type)
{
    assert(img && key && val);
    if (img==0 || key==0 || val==0) return -1;
    ImageWrapper *imgWrap = (ImageWrapper*)img;
    int rc = 2;

    Exiv2::IptcData &iptcData = imgWrap->image->iptcData();
    Exiv2::ExifData &exifData = imgWrap->image->exifData();

    std::string data(val);
    {
        size_t dataLen = data.length();
        // if data starts and ends with quotes, remove them
        if (dataLen > 1 && *(data.begin()) == '\"' && *(data.rbegin()) == '\"') {
            data = data.substr(1, dataLen-2);
        }
    }

    try {
        Exiv2::IptcKey iptcKey(key);
        rc = 1;

        if (type == invalidTypeId)
            type = (DllTypeId)Exiv2::IptcDataSets::dataSetType(iptcKey.tag(), iptcKey.record());
        Exiv2::Value::AutoPtr value = Exiv2::Value::create((Exiv2::TypeId)type);
        value->read(data);

        rc = iptcData.add(iptcKey, value.get());
    }
    catch(const Exiv2::AnyError&) {
    }

    if (rc) {
        // Failed with iptc, so try exif
        try {
            Exiv2::ExifKey exifKey(key);
            rc = 1;

            // No way to get value type for exif... string is the most common
            if (type == invalidTypeId)
                type = asciiString;
            Exiv2::Value::AutoPtr value = Exiv2::Value::create((Exiv2::TypeId)type);
            value->read(data);

            exifData.add(exifKey, value.get());
            rc = 0;
        }
        catch(const Exiv2::AnyError&) {
        }
    }

    return rc;
}
Exemple #2
0
bool KExiv2::setIptcTagsStringList(const char* iptcTagName, int maxSize,
                                   const QStringList& oldValues, const QStringList& newValues,
                                   bool setProgramName) const
{
    if (!setProgramId(setProgramName))
        return false;

    try
    {
        QStringList oldvals = oldValues;
        QStringList newvals = newValues;

        kDebug() << d->filePath.toAscii().constData() << " : " << iptcTagName
                      << " => " << newvals.join(",").toAscii().constData();

        // Remove all old values.
        Exiv2::IptcData iptcData(d->iptcMetadata());
        Exiv2::IptcData::iterator it = iptcData.begin();

        while(it != iptcData.end())
        {
            QString key = QString::fromLocal8Bit(it->key().c_str());
            QString val(it->toString().c_str());

            // Also remove new values to avoid duplicates. They will be added again below.
            if ( key == QString(iptcTagName) &&
                 (oldvals.contains(val) || newvals.contains(val))
               )
                it = iptcData.erase(it);
            else
                ++it;
        };

        // Add new values.

        Exiv2::IptcKey iptcTag(iptcTagName);

        for (QStringList::iterator it = newvals.begin(); it != newvals.end(); ++it)
        {
            QString key = *it;
            key.truncate(maxSize);

            Exiv2::Value::AutoPtr val = Exiv2::Value::create(Exiv2::string);
            val->read(key.toLatin1().constData());
            iptcData.add(iptcTag, val.get());
        }

        d->iptcMetadata() = iptcData;

        return true;
    }
    catch( Exiv2::Error& e )
    {
        d->printExiv2ExceptionError(QString("Cannot set Iptc key '%1' into image using Exiv2 ")
                                    .arg(iptcTagName), e);
    }

    return false;
}
Exemple #3
0
bool KExiv2::setIptcKeywords(const QStringList& oldKeywords, const QStringList& newKeywords,
                             bool setProgramName) const
{
    if (!setProgramId(setProgramName))
        return false;

    try
    {
        QStringList oldkeys = oldKeywords;
        QStringList newkeys = newKeywords;

        kDebug() << d->filePath.toAscii().constData()
                      << " ==> Iptc Keywords: " << newkeys.join(",").toAscii().constData();

        // Remove all old keywords.
        Exiv2::IptcData iptcData(d->iptcMetadata());
        Exiv2::IptcData::iterator it = iptcData.begin();

        while(it != iptcData.end())
        {
            QString key = QString::fromLocal8Bit(it->key().c_str());
            QString val(it->toString().c_str());

            // Also remove new keywords to avoid duplicates. They will be added again below.
            if ( key == QString("Iptc.Application2.Keywords") &&
                 (oldKeywords.contains(val) || newKeywords.contains(val)) )
                it = iptcData.erase(it);
            else
                ++it;
        };

        // Add new keywords. Note that Keywords Iptc tag is limited to 64 char but can be redondant.

        Exiv2::IptcKey iptcTag("Iptc.Application2.Keywords");

        for (QStringList::iterator it = newkeys.begin(); it != newkeys.end(); ++it)
        {
            QString key = *it;
            key.truncate(64);

            Exiv2::Value::AutoPtr val = Exiv2::Value::create(Exiv2::string);
            val->read(key.toLatin1().constData());
            iptcData.add(iptcTag, val.get());
        }

        d->iptcMetadata() = iptcData;

        return true;
    }
    catch( Exiv2::Error& e )
    {
        d->printExiv2ExceptionError("Cannot set Iptc Keywords into image using Exiv2 ", e);
    }

    return false;
}
Exemple #4
0
bool KExiv2::setIptcSubCategories(const QStringList& oldSubCategories, const QStringList& newSubCategories,
                                  bool setProgramName) const
{
    if (!setProgramId(setProgramName))
        return false;

    try
    {
        QStringList oldkeys = oldSubCategories;
        QStringList newkeys = newSubCategories;

        // Remove all old Sub Categories.
        Exiv2::IptcData iptcData(d->iptcMetadata());
        Exiv2::IptcData::iterator it = iptcData.begin();

        while(it != iptcData.end())
        {
            QString key = QString::fromLocal8Bit(it->key().c_str());
            QString val(it->toString().c_str());

            if (key == QString("Iptc.Application2.SuppCategory") && oldSubCategories.contains(val))
                it = iptcData.erase(it);
            else
                ++it;
        };

        // Add new Sub Categories. Note that SubCategories Iptc tag is limited to 32
        // characters but can be redondant.

        Exiv2::IptcKey iptcTag("Iptc.Application2.SuppCategory");

        for (QStringList::iterator it = newkeys.begin(); it != newkeys.end(); ++it)
        {
            QString key = *it;
            key.truncate(32);

            Exiv2::Value::AutoPtr val = Exiv2::Value::create(Exiv2::string);
            val->read(key.toLatin1().constData());
            iptcData.add(iptcTag, val.get());
        }

        d->iptcMetadata() = iptcData;

        return true;
    }
    catch( Exiv2::Error& e )
    {
        d->printExiv2ExceptionError("Cannot set Iptc Sub Categories into image using Exiv2 ", e);
    }

    return false;
}
Exemple #5
0
bool ImageTags::writeTagsToImage(QString &imageFileName, QSet<QString> &newTags)
{
	QSet<QString> imageTags;
	Exiv2::Image::AutoPtr exifImage;

	try {
		exifImage = Exiv2::ImageFactory::open(imageFileName.toStdString());
		exifImage->readMetadata();

		Exiv2::IptcData newIptcData;

		/* copy existing data */
		Exiv2::IptcData &iptcData = exifImage->iptcData();
		if (!iptcData.empty()) {
			QString key;
			Exiv2::IptcData::iterator end = iptcData.end();
			for (Exiv2::IptcData::iterator iptcIt = iptcData.begin(); iptcIt != end; ++iptcIt) {
				if (iptcIt->tagName() != "Keywords") {
					newIptcData.add(*iptcIt);
				}
	        }
	    }

		/* add new tags */
		QSetIterator<QString> newTagsIt(newTags);
		while (newTagsIt.hasNext()) {
			QString tag = newTagsIt.next();
		    Exiv2::Value::AutoPtr value = Exiv2::Value::create(Exiv2::string);
		    value->read(tag.toStdString());
		    Exiv2::IptcKey key("Iptc.Application2.Keywords");
		    newIptcData.add(key, value.get());
		}

	    exifImage->setIptcData(newIptcData);
	    exifImage->writeMetadata();
   	}
	catch (Exiv2::Error &error) {
		QMessageBox msgBox;
		msgBox.critical(this, tr("Error"), tr("Failed to save tags to ") + imageFileName);
		return false;
	}

	return true;
}
Exemple #6
0
int WriteFixedDatestamp(const char* File, time_t Time)
{
	// Write the GPS data to the file...

	struct stat statbuf;
	struct stat statbuf2;
	struct utimbuf utb;
	stat(File, &statbuf);

	Exiv2::Image::AutoPtr Image;

	try {
		Image = Exiv2::ImageFactory::open(File);
	} catch (Exiv2::Error e) {
		DEBUGLOG("Failed to open file %s.\n", File);
		return 0;
	}
	Image->readMetadata();
	if (Image.get() == NULL)
	{
		// It failed if we got here.
		DEBUGLOG("Failed to read file %s %s.\n",
			 File, Exiv2::strError().c_str());
		return 0;
	}
	
	Exiv2::ExifData &ExifToWrite = Image->exifData();
	
	const struct tm TimeStamp = *gmtime(&Time);
	char ScratchBuf[100];

	snprintf(ScratchBuf, sizeof(ScratchBuf), "%04d:%02d:%02d",
			TimeStamp.tm_year + 1900,
			TimeStamp.tm_mon + 1,
			TimeStamp.tm_mday);
	ExifToWrite.erase(ExifToWrite.findKey(Exiv2::ExifKey("Exif.GPSInfo.GPSDateStamp")));
	ExifToWrite["Exif.GPSInfo.GPSDateStamp"] = ScratchBuf;

	Exiv2::Value::AutoPtr Value = Exiv2::Value::create(Exiv2::unsignedRational);
	snprintf(ScratchBuf, sizeof(ScratchBuf), "%d/1 %d/1 %d/1",
			TimeStamp.tm_hour, TimeStamp.tm_min,
			TimeStamp.tm_sec);
	Value->read(ScratchBuf);
	ExifToWrite.erase(ExifToWrite.findKey(Exiv2::ExifKey("Exif.GPSInfo.GPSTimeStamp")));
	ExifToWrite.add(Exiv2::ExifKey("Exif.GPSInfo.GPSTimeStamp"), Value.get());
	
	try {
		Image->writeMetadata();
	} catch (Exiv2::Error e) {
		DEBUGLOG("Failed to write to file %s.\n", File);
		return 0;
	}
	
	// Reset the mtime.
	stat(File, &statbuf2);
	utb.actime = statbuf2.st_atime;
	utb.modtime = statbuf.st_mtime;
	utime(File, &utb);

	return 1;
}
Exemple #7
0
int WriteGPSData(const char* File, const struct GPSPoint* Point,
		 const char* Datum, int NoChangeMtime, int DegMinSecs)
{
	// Write the GPS data to the file...

	struct stat statbuf;
	struct stat statbuf2;
	struct utimbuf utb;
	if (NoChangeMtime)
		stat(File, &statbuf);
	Exiv2::Image::AutoPtr Image;

	try {
		Image = Exiv2::ImageFactory::open(File);
	} catch (Exiv2::Error e) {
		DEBUGLOG("Failed to open file %s.\n", File);
		return 0;
	}
	Image->readMetadata();
	if (Image.get() == NULL)
	{
		// It failed if we got here.
		DEBUGLOG("Failed to read file %s %s.\n",
			 File, Exiv2::strError().c_str());
		return 0;
	}
	
	Exiv2::ExifData &ExifToWrite = Image->exifData();

	// Make sure we're starting from a clean GPS IFD.
	// There might be lots of GPS tags existing here, since only the
	// presence of the GPSLatitude tag causes correlation to stop with
	// "GPS Already Present" error.
	EraseGpsTags(ExifToWrite);

	char ScratchBuf[100];

	// Do all the easy constant ones first.
	// GPSVersionID tag: standard says it should be four bytes: 02 02 00 00
	//  (and, must be present).
	Exiv2::Value::AutoPtr Value = Exiv2::Value::create(Exiv2::unsignedByte);
	Value->read("2 2 0 0");
	ExifToWrite.add(Exiv2::ExifKey("Exif.GPSInfo.GPSVersionID"), Value.get());
	// Datum: the datum of the measured data. The default is WGS-84.
	if (*Datum)
		ExifToWrite["Exif.GPSInfo.GPSMapDatum"] = Datum;
	
	// Now start adding data.
	// ALTITUDE.
	// If no altitude was found in the GPX file, ElevDecimals will be -1
	if (Point->ElevDecimals >= 0) {
		// Altitude reference: byte "00" meaning "sea level".
		// Or "01" if the altitude value is negative.
		Value = Exiv2::Value::create(Exiv2::unsignedByte);
		if (Point->Elev >= 0)
		{
			Value->read("0");
		} else {
			Value->read("1");
		}
		ExifToWrite.add(Exiv2::ExifKey("Exif.GPSInfo.GPSAltitudeRef"), Value.get());
		// And the actual altitude.
		Value = Exiv2::Value::create(Exiv2::unsignedRational);
		// 3 decimal points is beyond the limit of current GPS technology
		int Decimals = MIN(Point->ElevDecimals, 3);
		ConvertToRational(fabs(Point->Elev), Decimals, ScratchBuf, sizeof(ScratchBuf));

		/* printf("Altitude: %f -> %s\n", Point->Elev, ScratchBuf); */
		Value->read(ScratchBuf);
		ExifToWrite.add(Exiv2::ExifKey("Exif.GPSInfo.GPSAltitude"), Value.get());
	}
	
	// LATITUDE
	// Latitude reference: "N" or "S".
	if (Point->Lat < 0)
	{
		// Less than Zero: ie, minus: means
		// Southern hemisphere. Where I live.
		ExifToWrite["Exif.GPSInfo.GPSLatitudeRef"] = "S";
	} else {
		// More than Zero: ie, plus: means
		// Northern hemisphere.
		ExifToWrite["Exif.GPSInfo.GPSLatitudeRef"] = "N";
	}
	// Now the actual latitude itself.
	// The original comment read:
	// This is done as three rationals.
	// I choose to do it as:
	//   dd/1 - degrees.
	//   mmmm/100 - minutes
	//   0/1 - seconds
	// Exif standard says you can do it with minutes
	// as mm/1 and then seconds as ss/1, but its
	// (slightly) more accurate to do it as
	//  mmmm/100 than to split it.
	// We also absolute the value (with fabs())
	// as the sign is encoded in LatRef.
	// Further note: original code did not translate between
	//   dd.dddddd to dd mm.mm - that's why we now multiply
	//   by 60*N - x60 to get minutes, xN to get to mmmm/N.
	//   N is 10^S where S is the number of significant decimal
	//   places.
	//
	// Rereading the EXIF standard, it's quite ok to do DD MM SS.SS
	// Which is much more accurate. This is the new default, unless otherwise
	// set.
	Value = Exiv2::Value::create(Exiv2::unsignedRational);

	if (DegMinSecs)
	{
		ConvertToLatLongRational(Point->Lat, Point->LatDecimals, ScratchBuf, sizeof(ScratchBuf));
	} else {
		ConvertToOldLatLongRational(Point->Lat, ScratchBuf, sizeof(ScratchBuf));
	}
	Value->read(ScratchBuf);
	ExifToWrite.add(Exiv2::ExifKey("Exif.GPSInfo.GPSLatitude"), Value.get());
	
	// LONGITUDE
	// Longitude reference: "E" or "W".
	if (Point->Long < 0)
	{
		// Less than Zero: ie, minus: means
		// Western hemisphere.
		ExifToWrite["Exif.GPSInfo.GPSLongitudeRef"] = "W";
	} else {
		// More than Zero: ie, plus: means
		// Eastern hemisphere. Where I live.
		ExifToWrite["Exif.GPSInfo.GPSLongitudeRef"] = "E";
	}
	// Now the actual longitude itself, in the same way as latitude
	Value = Exiv2::Value::create(Exiv2::unsignedRational);

	if (DegMinSecs)
	{
		ConvertToLatLongRational(Point->Long, Point->LongDecimals, ScratchBuf, sizeof(ScratchBuf));
	} else {
		ConvertToOldLatLongRational(Point->Long, ScratchBuf, sizeof(ScratchBuf));
	}
	Value->read(ScratchBuf);
	ExifToWrite.add(Exiv2::ExifKey("Exif.GPSInfo.GPSLongitude"), Value.get());

	// The timestamp.
	// Make up the timestamp...
	// The timestamp is taken as the UTC time of the photo.
	// If interpolation occurred, then this time is the time of the photo.
	struct tm TimeStamp = *gmtime(&(Point->Time));

	Value = Exiv2::Value::create(Exiv2::unsignedRational);
	snprintf(ScratchBuf, sizeof(ScratchBuf), "%d/1 %d/1 %d/1",
			TimeStamp.tm_hour, TimeStamp.tm_min,
			TimeStamp.tm_sec);
	Value->read(ScratchBuf);
	ExifToWrite.add(Exiv2::ExifKey("Exif.GPSInfo.GPSTimeStamp"), Value.get());

	// And we should also do a datestamp.
	snprintf(ScratchBuf, sizeof(ScratchBuf), "%04d:%02d:%02d",
			TimeStamp.tm_year + 1900,
			TimeStamp.tm_mon + 1,
			TimeStamp.tm_mday);
	ExifToWrite["Exif.GPSInfo.GPSDateStamp"] = ScratchBuf;

	// Write the data to file.
	try {
		Image->writeMetadata();
	} catch (Exiv2::Error e) {
		DEBUGLOG("Failed to write to file %s.\n", File);
		return 0;
	}

	if (NoChangeMtime)
	{
		stat(File, &statbuf2);
		utb.actime = statbuf2.st_atime;
		utb.modtime = statbuf.st_mtime;
		utime(File, &utb);
	}

	return 1;
	
}
Exemple #8
0
int main()
try {
    // The XMP property container
    Exiv2::XmpData xmpData;

    // -------------------------------------------------------------------------
    // Teaser: Setting XMP properties doesn't get much easier than this:

    xmpData["Xmp.dc.source"]  = "xmpsample.cpp";    // a simple text value
    xmpData["Xmp.dc.subject"] = "Palmtree";         // an array item
    xmpData["Xmp.dc.subject"] = "Rubbertree";       // add a 2nd array item
    // a language alternative with two entries and without default
    xmpData["Xmp.dc.title"]   = "lang=de-DE Sonnenuntergang am Strand";
    xmpData["Xmp.dc.title"]   = "lang=en-US Sunset on the beach";

    // -------------------------------------------------------------------------
    // Any properties can be set provided the namespace is known. Values of any
    // type can be assigned to an Xmpdatum, if they have an output operator. The
    // default XMP value type for unknown properties is a simple text value.

    xmpData["Xmp.dc.one"]     = -1;
    xmpData["Xmp.dc.two"]     = 3.1415;
    xmpData["Xmp.dc.three"]   = Exiv2::Rational(5, 7);
    xmpData["Xmp.dc.four"]    = uint16_t(255);
    xmpData["Xmp.dc.five"]    = int32_t(256);
    xmpData["Xmp.dc.six"]     = false;

    // In addition, there is a dedicated assignment operator for Exiv2::Value
    Exiv2::XmpTextValue val("Seven");
    xmpData["Xmp.dc.seven"]   = val;
    xmpData["Xmp.dc.eight"]   = true;

    // Extracting values
    assert(xmpData["Xmp.dc.one"].toLong() == -1);
    assert(xmpData["Xmp.dc.one"].value().ok());

    const Exiv2::Value &getv1 = xmpData["Xmp.dc.one"].value();
    assert(isEqual(getv1.toFloat(), -1)); 
    assert(getv1.ok());
    assert(getv1.toRational() == Exiv2::Rational(-1, 1));
    assert(getv1.ok());

    const Exiv2::Value &getv2 = xmpData["Xmp.dc.two"].value();
    assert(isEqual(getv2.toFloat(), 3.1415f)); 
    assert(getv2.ok());
    assert(getv2.toLong() == 3);
    assert(getv2.ok());
    Exiv2::Rational R = getv2.toRational();
    assert(getv2.ok());
    assert(isEqual(static_cast<float>(R.first) / R.second, 3.1415f ));

    const Exiv2::Value &getv3 = xmpData["Xmp.dc.three"].value();
    assert(isEqual(getv3.toFloat(), 5.0f/7.0f)); 
    assert(getv3.ok());
    assert(getv3.toLong() == 0);  // long(5.0 / 7.0) 
    assert(getv3.ok());
    assert(getv3.toRational() == Exiv2::Rational(5, 7));
    assert(getv3.ok());
    
    const Exiv2::Value &getv6 = xmpData["Xmp.dc.six"].value();
    assert(getv6.toLong() == 0);
    assert(getv6.ok());
    assert(getv6.toFloat() == 0.0);
    assert(getv6.ok());
    assert(getv6.toRational() == Exiv2::Rational(0, 1));
    assert(getv6.ok());
    
    const Exiv2::Value &getv7 = xmpData["Xmp.dc.seven"].value();
    getv7.toLong(); // this should fail
    assert(!getv7.ok()); 

    const Exiv2::Value &getv8 = xmpData["Xmp.dc.eight"].value();
    assert(getv8.toLong() == 1);
    assert(getv8.ok());
    assert(getv8.toFloat() == 1.0);
    assert(getv8.ok());
    assert(getv8.toRational() == Exiv2::Rational(1, 1));
    assert(getv8.ok());

    // Deleting an XMP property
    Exiv2::XmpData::iterator pos = xmpData.findKey(Exiv2::XmpKey("Xmp.dc.eight"));
    if (pos == xmpData.end()) throw Exiv2::Error(1, "Key not found");
    xmpData.erase(pos);

    // -------------------------------------------------------------------------
    // Exiv2 has specialized values for simple XMP properties, arrays of simple
    // properties and language alternatives.

    // Add a simple XMP property in a known namespace    
    Exiv2::Value::AutoPtr v = Exiv2::Value::create(Exiv2::xmpText);
    v->read("image/jpeg");
    xmpData.add(Exiv2::XmpKey("Xmp.dc.format"), v.get());

    // Add an ordered array of text values.
    v = Exiv2::Value::create(Exiv2::xmpSeq); // or xmpBag or xmpAlt.
    v->read("1) The first creator");         // The sequence in which the array
    v->read("2) The second creator");        // elements are added is their
    v->read("3) And another one");           // order in the array.
    xmpData.add(Exiv2::XmpKey("Xmp.dc.creator"), v.get());

    // Add a language alternative property
    v = Exiv2::Value::create(Exiv2::langAlt);
    v->read("lang=de-DE Hallo, Welt");       // The default doesn't need a 
    v->read("Hello, World");                 // qualifier
    xmpData.add(Exiv2::XmpKey("Xmp.dc.description"), v.get());

    // According to the XMP specification, Xmp.tiff.ImageDescription is an
    // alias for Xmp.dc.description. Exiv2 treats an alias just like any
    // other property and leaves it to the application to implement specific
    // behaviour if desired.
    xmpData["Xmp.tiff.ImageDescription"] = "TIFF image description";
    xmpData["Xmp.tiff.ImageDescription"] = "lang=de-DE TIFF Bildbeschreibung";

    // -------------------------------------------------------------------------
    // Register a namespace which Exiv2 doesn't know yet. This is only needed
    // when properties are added manually. If the XMP metadata is read from an
    // image, namespaces are decoded and registered at the same time.
    Exiv2::XmpProperties::registerNs("myNamespace/", "ns");

    // -------------------------------------------------------------------------
    // Add a property in the new custom namespace.
    xmpData["Xmp.ns.myProperty"] = "myValue";

    // -------------------------------------------------------------------------
    // There are no specialized values for structures, qualifiers and nested
    // types. However, these can be added by using an XmpTextValue and a path as
    // the key.

    // Add a structure
    Exiv2::XmpTextValue tv("16");
    xmpData.add(Exiv2::XmpKey("Xmp.xmpDM.videoFrameSize/stDim:w"), &tv);
    tv.read("9");
    xmpData.add(Exiv2::XmpKey("Xmp.xmpDM.videoFrameSize/stDim:h"), &tv);
    tv.read("inch");
    xmpData.add(Exiv2::XmpKey("Xmp.xmpDM.videoFrameSize/stDim:unit"), &tv);

    // Add an element with a qualifier (using the namespace registered above)
    xmpData["Xmp.dc.publisher"] = "James Bond";  // creates an unordered array
    xmpData["Xmp.dc.publisher[1]/?ns:role"] = "secret agent";

    // Add a qualifer to an array element of Xmp.dc.creator (added above)
    tv.read("programmer");
    xmpData.add(Exiv2::XmpKey("Xmp.dc.creator[2]/?ns:role"), &tv);

    // Add an array of structures
    tv.read("");                                         // Clear the value
    tv.setXmpArrayType(Exiv2::XmpValue::xaBag);
    xmpData.add(Exiv2::XmpKey("Xmp.xmpBJ.JobRef"), &tv); // Set the array type.

    tv.setXmpArrayType(Exiv2::XmpValue::xaNone);
    tv.read("Birthday party");
    xmpData.add(Exiv2::XmpKey("Xmp.xmpBJ.JobRef[1]/stJob:name"), &tv);
    tv.read("Photographer");
    xmpData.add(Exiv2::XmpKey("Xmp.xmpBJ.JobRef[1]/stJob:role"), &tv);

    tv.read("Wedding ceremony");
    xmpData.add(Exiv2::XmpKey("Xmp.xmpBJ.JobRef[2]/stJob:name"), &tv);
    tv.read("Best man");
    xmpData.add(Exiv2::XmpKey("Xmp.xmpBJ.JobRef[2]/stJob:role"), &tv);

    // -------------------------------------------------------------------------
    // Output XMP properties
    for (Exiv2::XmpData::const_iterator md = xmpData.begin(); 
         md != xmpData.end(); ++md) {
        std::cout << std::setfill(' ') << std::left
                  << std::setw(44)
                  << md->key() << " "
                  << std::setw(9) << std::setfill(' ') << std::left
                  << md->typeName() << " "
                  << std::dec << std::setw(3)
                  << std::setfill(' ') << std::right
                  << md->count() << "  "
                  << std::dec << md->value()
                  << std::endl;
    }

    // -------------------------------------------------------------------------
    // Serialize the XMP data and output the XMP packet
    std::string xmpPacket;
    if (0 != Exiv2::XmpParser::encode(xmpPacket, xmpData)) {
        throw Exiv2::Error(1, "Failed to serialize XMP data");
    }
    std::cout << xmpPacket << "\n";

    // Cleanup
    Exiv2::XmpParser::terminate();

    return 0;
}
catch (Exiv2::AnyError& e) {
    std::cout << "Caught Exiv2 exception '" << e << "'\n";
    return -1;
}
Exemple #9
0
bool KExiv2::setImageDateTime(const QDateTime& dateTime, bool setDateTimeDigitized, bool setProgramName) const
{
    if(!dateTime.isValid())
        return false;

    if (!setProgramId(setProgramName))
        return false;

    try
    {
        // In first we write date & time into Exif.

        // DateTimeDigitized is set by slide scanners etc. when a picture is digitized.
        // DateTimeOriginal specifies the date/time when the picture was taken.
        // For digital cameras, these dates should be both set, and identical.
        // Reference: http://www.exif.org/Exif2-2.PDF, chapter 4.6.5, table 4, section F.

        const std::string &exifdatetime(dateTime.toString(QString("yyyy:MM:dd hh:mm:ss")).toAscii().constData());
        d->exifMetadata()["Exif.Image.DateTime"]         = exifdatetime;
        d->exifMetadata()["Exif.Photo.DateTimeOriginal"] = exifdatetime;
        if(setDateTimeDigitized)
            d->exifMetadata()["Exif.Photo.DateTimeDigitized"] = exifdatetime;

#ifdef _XMP_SUPPORT_

        // In second we write date & time into Xmp.

        const std::string &xmpdatetime(dateTime.toString(Qt::ISODate).toAscii().constData());

        Exiv2::Value::AutoPtr xmpTxtVal = Exiv2::Value::create(Exiv2::xmpText);
        xmpTxtVal->read(xmpdatetime);
        d->xmpMetadata().add(Exiv2::XmpKey("Xmp.exif.DateTimeOriginal"), xmpTxtVal.get());
        d->xmpMetadata().add(Exiv2::XmpKey("Xmp.photoshop.DateCreated"), xmpTxtVal.get());
        d->xmpMetadata().add(Exiv2::XmpKey("Xmp.tiff.DateTime"),         xmpTxtVal.get());
        d->xmpMetadata().add(Exiv2::XmpKey("Xmp.xmp.CreateDate"),        xmpTxtVal.get());
        d->xmpMetadata().add(Exiv2::XmpKey("Xmp.xmp.MetadataDate"),      xmpTxtVal.get());
        d->xmpMetadata().add(Exiv2::XmpKey("Xmp.xmp.ModifyDate"),        xmpTxtVal.get());
        if(setDateTimeDigitized)
            d->xmpMetadata().add(Exiv2::XmpKey("Xmp.exif.DateTimeDigitized"), xmpTxtVal.get());

        // Tag not updated:
        // "Xmp.dc.DateTime" is a sequence of date relevant of dublin core change.
        //                   This is not the picture date as well

#endif // _XMP_SUPPORT_

        // In third we write date & time into Iptc.

        const std::string &iptcdate(dateTime.date().toString(Qt::ISODate).toAscii().constData());
        const std::string &iptctime(dateTime.time().toString(Qt::ISODate).toAscii().constData());
        d->iptcMetadata()["Iptc.Application2.DateCreated"] = iptcdate;
        d->iptcMetadata()["Iptc.Application2.TimeCreated"] = iptctime;
        if(setDateTimeDigitized)
        {
            d->iptcMetadata()["Iptc.Application2.DigitizationDate"] = iptcdate;
            d->iptcMetadata()["Iptc.Application2.DigitizationTime"] = iptctime;
        }

        return true;
    }
    catch( Exiv2::Error& e )
    {
        d->printExiv2ExceptionError("Cannot set Date & Time into image using Exiv2 ", e);
    }
    catch(...)
    {
        kError() << "Default exception from Exiv2";
    }

    return false;
}
Exemple #10
0
// Overwrites existing value if found, otherwise creates a new one.
// Passing invalidTypeId causes the type to be guessed.
// Guessing types is accurate for IPTC, but not for EXIF.
// Returns 0 on success
EXIVSIMPLE_API int ModifyMeta(HIMAGE img, const char *key, const char *val, DllTypeId type)
{
    assert(img && key && val);
    if (img==0 || key==0 || val==0) return -1;
    ImageWrapper *imgWrap = (ImageWrapper*)img;
    int rc = 2;

    Exiv2::IptcData &iptcData = imgWrap->image->iptcData();
    Exiv2::ExifData &exifData = imgWrap->image->exifData();

    std::string data(val);
    // 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);
    }

    try {
        Exiv2::IptcKey iptcKey(key);
        rc = 1;

        if (type == invalidTypeId)
            type = (DllTypeId)Exiv2::IptcDataSets::dataSetType(iptcKey.tag(), iptcKey.record());
        Exiv2::Value::AutoPtr value = Exiv2::Value::create((Exiv2::TypeId)type);
        value->read(data);

        Exiv2::IptcData::iterator iter = iptcData.findKey(iptcKey);
        if (iter != iptcData.end()) {
            iter->setValue(value.get());
            rc = 0;
        }
        else {
            rc = iptcData.add(iptcKey, value.get());
        }
    } 
    catch(const Exiv2::AnyError&) {
    }

    if (rc) {
        // Failed with iptc, so try exif
        try {
            Exiv2::ExifKey exifKey(key);
            rc = 1;

            // No way to get value type for exif... string is the most common
            if (type == invalidTypeId)
                type = asciiString;
            Exiv2::Value::AutoPtr value = Exiv2::Value::create((Exiv2::TypeId)type);
            value->read(data);

            Exiv2::ExifData::iterator iter = exifData.findKey(exifKey);
            if (iter != exifData.end()) {
                iter->setValue(value.get());
                rc = 0;
            }
            else {
                exifData.add(exifKey, value.get());
                rc = 0;
            }
        }
        catch(const Exiv2::AnyError&) {
        }
    }

    return rc;
}
Exemple #11
0
    void AsfVideo::metadataHandler(int meta)
    {
        DataBuf buf(5000);
        io_->read(buf.pData_, 2);
        int recordCount = Exiv2::getUShort(buf.pData_, littleEndian), nameLength = 0, dataLength = 0, dataType = 0;
        Exiv2::Value::AutoPtr v = Exiv2::Value::create(Exiv2::xmpSeq);
        byte guidBuf[16];   char fileID[37] = "";

        while(recordCount--) {
            std::memset(buf.pData_, 0x0, buf.size_);

            if(meta == 1 || meta == 3) {
                io_->read(buf.pData_, 4);
                io_->read(buf.pData_, 2);
                nameLength = Exiv2::getUShort(buf.pData_, littleEndian);
                io_->read(buf.pData_, 2);
                dataType = Exiv2::getUShort(buf.pData_, littleEndian);
                io_->read(buf.pData_, 4);
                dataLength = Exiv2::getULong(buf.pData_, littleEndian);

                if (nameLength > 5000) {
#ifndef SUPPRESS_WARNINGS
                    EXV_ERROR << "Xmp.video.Metadata nameLength was found to be larger than 5000 "
                              << " entries considered invalid; not read.\n";
#endif
                    io_->seek(io_->tell() + nameLength, BasicIo::beg);
                }
            else
                io_->read(buf.pData_, nameLength);

                v->read(toString16(buf));
                if(dataType == 6) {
                    io_->read(guidBuf, 16);
                    getGUID(guidBuf, fileID);
                }
                else
                    // Sanity check with an "unreasonably" large number
                    if (dataLength > 5000) {
#ifndef SUPPRESS_WARNINGS
                        EXV_ERROR << "Xmp.video.Metadata dataLength was found to be larger than 5000 "
                                  << " entries considered invalid; not read.\n";
#endif
                        io_->seek(io_->tell() + dataLength, BasicIo::beg);
                    }
                else
                        io_->read(buf.pData_, dataLength);
            }

            else if(meta == 2) {
                io_->read(buf.pData_, 2);
                nameLength = Exiv2::getUShort(buf.pData_, littleEndian);

                if (nameLength > 5000) {
#ifndef SUPPRESS_WARNINGS
                    EXV_ERROR << "Xmp.video.Metadata nameLength was found to be larger than 5000 "
                              << " entries considered invalid; not read.\n";
#endif
                    io_->seek(io_->tell() + nameLength, BasicIo::beg);
                }
            else
                io_->read(buf.pData_, nameLength);

                v->read(toString16(buf));

                io_->read(buf.pData_, 2);
                dataType = Exiv2::getUShort(buf.pData_, littleEndian);

                io_->read(buf.pData_, 2);
                dataLength = Exiv2::getUShort(buf.pData_, littleEndian);

                // Sanity check with an "unreasonably" large number
                if (dataLength > 5000) {
#ifndef SUPPRESS_WARNINGS
                    EXV_ERROR << "Xmp.video.Metadata dataLength was found to be larger than 5000 "
                              << " entries considered invalid; not read.\n";
#endif
                    io_->seek(io_->tell() + dataLength, BasicIo::beg);
                }
            else
                io_->read(buf.pData_, dataLength);
            }

            if(dataType == 0) {                       // Unicode String
                v->read(toString16(buf));
            }
            else if(dataType == 2 || dataType == 5) { // 16-bit Unsigned Integer
                v->read( Exiv2::toString( Exiv2::getUShort(buf.pData_, littleEndian)));
            }
            else if(dataType == 3) {                  // 32-bit Unsigned Integer
                v->read( Exiv2::toString( Exiv2::getULong( buf.pData_, littleEndian)));
            }
            else if(dataType == 4) {                  // 64-bit Unsigned Integer
                v->read(Exiv2::toString(getUint64_t(buf)));
            }
            else if(dataType == 6) {                  // 128-bit GUID
                v->read(Exiv2::toString(fileID));
            }
            else {                                    // Byte array
                v->read( Exiv2::toString(buf.pData_));
            }
        }

        if(meta == 1) {
            xmpData_.add(Exiv2::XmpKey("Xmp.video.Metadata"), v.get());
        }
        else if(meta == 2) {
            xmpData_.add(Exiv2::XmpKey("Xmp.video.ExtendedContentDescription"), v.get());
        }
        else {
            xmpData_.add(Exiv2::XmpKey("Xmp.video.MetadataLibrary"), v.get());
        }
    } // AsfVideo::metadataHandler
Exemple #12
0
    void AsfVideo::tagDecoder(const TagVocabulary *tv, uint64_t size)
    {
        uint64_t cur_pos = io_->tell();
        DataBuf buf(1000);
        unsigned long count = 0, tempLength = 0;
        buf.pData_[4] = '\0' ;
        Exiv2::Value::AutoPtr v = Exiv2::Value::create(Exiv2::xmpSeq);

        if(compareTag( exvGettext(tv->label_), "Header")) {
            localPosition_ = 0;
            io_->read(buf.pData_, 4);
            io_->read(buf.pData_, 2);

            while(localPosition_ < cur_pos + size) decodeBlock();
        }

        else if(compareTag( exvGettext(tv->label_), "File_Properties"))
            fileProperties();

        else if(compareTag( exvGettext(tv->label_), "Stream_Properties"))
            streamProperties();

        else if(compareTag( exvGettext(tv->label_), "Metadata"))
            metadataHandler(1);

        else if(compareTag( exvGettext(tv->label_), "Extended_Content_Description"))
            metadataHandler(2);

        else if(compareTag( exvGettext(tv->label_), "Metadata_Library"))
            metadataHandler(3);

        else if(compareTag( exvGettext(tv->label_), "Codec_List"))
            codecList();

        else if(compareTag( exvGettext(tv->label_), "Content_Description"))
            contentDescription(size);

        else if(compareTag( exvGettext(tv->label_), "Extended_Stream_Properties"))
            extendedStreamProperties(size);

        else if(compareTag( exvGettext(tv->label_), "Header_Extension")) {
            localPosition_ = 0;
            headerExtension(size);
        }

        else if(compareTag( exvGettext(tv->label_), "Language_List")) {
            std::memset(buf.pData_, 0x0, buf.size_);
            io_->read(buf.pData_, 2);
            count = Exiv2::getUShort(buf.pData_, littleEndian);

            while(count--) {
                std::memset(buf.pData_, 0x0, buf.size_);
                io_->read(buf.pData_, 1);   tempLength = (int)buf.pData_[0];

                io_->read(buf.pData_, tempLength);
                v->read(toString16(buf));
            }
            xmpData_.add(Exiv2::XmpKey("Xmp.video.TrackLang"), v.get());
        }

        io_->seek(cur_pos + size, BasicIo::beg);
        localPosition_ = io_->tell();
    } // AsfVideo::tagDecoder