summaryrefslogtreecommitdiffstats
path: root/src/libs/dmetadata/dmetadata.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/dmetadata/dmetadata.cpp')
-rw-r--r--src/libs/dmetadata/dmetadata.cpp544
1 files changed, 544 insertions, 0 deletions
diff --git a/src/libs/dmetadata/dmetadata.cpp b/src/libs/dmetadata/dmetadata.cpp
new file mode 100644
index 00000000..bb37506c
--- /dev/null
+++ b/src/libs/dmetadata/dmetadata.cpp
@@ -0,0 +1,544 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2006-02-23
+ * Description : image metadata interface
+ *
+ * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ * Copyright (C) 2006-2007 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
+ *
+ * This program is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation;
+ * either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * ============================================================ */
+
+// TQt includes.
+
+#include <tqdom.h>
+#include <tqfile.h>
+
+// LibKDcraw includes.
+
+#include <libkdcraw/dcrawinfocontainer.h>
+#include <libkdcraw/kdcraw.h>
+
+// Local includes.
+
+#include "daboutdata.h"
+#include "constants.h"
+#include "ddebug.h"
+#include "dmetadata.h"
+
+namespace Digikam
+{
+
+DMetadata::DMetadata()
+ : KExiv2Iface::KExiv2()
+{
+}
+
+DMetadata::DMetadata(const TQString& filePath)
+ : KExiv2Iface::KExiv2()
+{
+ load(filePath);
+}
+
+DMetadata::~DMetadata()
+{
+}
+
+bool DMetadata::load(const TQString& filePath)
+{
+ // In first, we trying to get metadata using Exiv2,
+ // else we will use dcraw to extract minimal information.
+
+ if (!KExiv2::load(filePath))
+ {
+ if (!loadUsingDcraw(filePath))
+ return false;
+ }
+
+ return true;
+}
+
+bool DMetadata::loadUsingDcraw(const TQString& filePath)
+{
+ KDcrawIface::DcrawInfoContainer identify;
+ if (KDcrawIface::KDcraw::rawFileIdentify(identify, filePath))
+ {
+ long int num=1, den=1;
+
+ if (!identify.model.isNull())
+ setExifTagString("Exif.Image.Model", identify.model.latin1(), false);
+
+ if (!identify.make.isNull())
+ setExifTagString("Exif.Image.Make", identify.make.latin1(), false);
+
+ if (!identify.owner.isNull())
+ setExifTagString("Exif.Image.Artist", identify.owner.latin1(), false);
+
+ if (identify.sensitivity != -1)
+ setExifTagLong("Exif.Photo.ISOSpeedRatings", identify.sensitivity, false);
+
+ if (identify.dateTime.isValid())
+ setImageDateTime(identify.dateTime, false, false);
+
+ if (identify.exposureTime != -1.0)
+ {
+ convertToRational(1/identify.exposureTime, &num, &den, 8);
+ setExifTagRational("Exif.Photo.ExposureTime", num, den, false);
+ }
+
+ if (identify.aperture != -1.0)
+ {
+ convertToRational(identify.aperture, &num, &den, 8);
+ setExifTagRational("Exif.Photo.ApertureValue", num, den, false);
+ }
+
+ if (identify.focalLength != -1.0)
+ {
+ convertToRational(identify.focalLength, &num, &den, 8);
+ setExifTagRational("Exif.Photo.FocalLength", num, den, false);
+ }
+
+ if (identify.imageSize.isValid())
+ setImageDimensions(identify.imageSize, false);
+
+ // A RAW image is always uncalibrated. */
+ setImageColorWorkSpace(WORKSPACE_UNCALIBRATED, false);
+
+ return true;
+ }
+
+ return false;
+}
+
+TQString DMetadata::getImageComment() const
+{
+ if (getFilePath().isEmpty())
+ return TQString();
+
+ // In first we trying to get image comments, outside of Exif and IPTC.
+
+ TQString comment = getCommentsDecoded();
+ if (!comment.isEmpty())
+ return comment;
+
+ // In second, we trying to get Exif comments
+
+ if (!getExif().isEmpty())
+ {
+ TQString exifComment = getExifComment();
+ if (!exifComment.isEmpty())
+ return exifComment;
+ }
+
+ // In third, we trying to get IPTC comments
+
+ if (!getIptc().isEmpty())
+ {
+ TQString iptcComment = getIptcTagString("Iptc.Application2.Caption", false);
+ if (!iptcComment.isEmpty() && !iptcComment.stripWhiteSpace().isEmpty())
+ return iptcComment;
+ }
+
+ return TQString();
+}
+
+bool DMetadata::setImageComment(const TQString& comment)
+{
+ //See bug #139313: An empty string is also a valid value
+ //if (comment.isEmpty())
+ // return false;
+
+ DDebug() << getFilePath() << " ==> Comment: " << comment << endl;
+
+ if (!setProgramId())
+ return false;
+
+ // In first we trying to set image comments, outside of Exif and IPTC.
+
+ if (!setComments(comment.utf8()))
+ return false;
+
+ // In Second we write comments into Exif.
+
+ if (!setExifComment(comment))
+ return false;
+
+ // In Third we write comments into Iptc.
+ // Note that Caption IPTC tag is limited to 2000 char and ASCII charset.
+
+ TQString commentIptc = comment;
+ commentIptc.truncate(2000);
+
+ if (!setIptcTagString("Iptc.Application2.Caption", commentIptc))
+ return false;
+
+ return true;
+}
+
+/*
+Iptc.Application2.Urgency <==> digiKam Rating links:
+
+digiKam IPTC
+Rating Urgency
+
+0 star <=> 8 // Least important
+1 star <=> 7
+1 star <== 6
+2 star <=> 5
+3 star <=> 4
+4 star <== 3
+4 star <=> 2
+5 star <=> 1 // Most important
+*/
+
+int DMetadata::getImageRating() const
+{
+ if (getFilePath().isEmpty())
+ return -1;
+
+ // Check Exif rating tag set by Windows Vista
+ // Note : no need to check rating in percent tags (Exif.image.0x4747) here because
+ // its appear always with rating tag value (Exif.image.0x4749).
+
+ if (!getExif().isEmpty())
+ {
+ long rating = -1;
+ if (getExifTagLong("Exif.Image.0x4746", rating))
+ {
+ if (rating >= RatingMin && rating <= RatingMax)
+ return rating;
+ }
+ }
+
+ // Check Iptc Urgency tag content
+
+ if (!getIptc().isEmpty())
+ {
+ TQString IptcUrgency(getIptcTagData("Iptc.Application2.Urgency"));
+
+ if (!IptcUrgency.isEmpty())
+ {
+ if (IptcUrgency == TQString("1"))
+ return 5;
+ else if (IptcUrgency == TQString("2"))
+ return 4;
+ else if (IptcUrgency == TQString("3"))
+ return 4;
+ else if (IptcUrgency == TQString("4"))
+ return 3;
+ else if (IptcUrgency == TQString("5"))
+ return 2;
+ else if (IptcUrgency == TQString("6"))
+ return 1;
+ else if (IptcUrgency == TQString("7"))
+ return 1;
+ else if (IptcUrgency == TQString("8"))
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+bool DMetadata::setImageRating(int rating)
+{
+ if (rating < RatingMin || rating > RatingMax)
+ {
+ DDebug() << k_funcinfo << "Rating value to write is out of range!" << endl;
+ return false;
+ }
+
+ DDebug() << getFilePath() << " ==> Rating: " << rating << endl;
+
+ if (!setProgramId())
+ return false;
+
+ // Set Exif rating tag used by Windows Vista.
+
+ if (!setExifTagLong("Exif.Image.0x4746", rating))
+ return false;
+
+ // Wrapper around rating percents managed by Windows Vista.
+ int ratePercents = 0;
+ switch(rating)
+ {
+ case 0:
+ ratePercents = 0;
+ break;
+ case 1:
+ ratePercents = 1;
+ break;
+ case 2:
+ ratePercents = 25;
+ break;
+ case 3:
+ ratePercents = 50;
+ break;
+ case 4:
+ ratePercents = 75;
+ break;
+ case 5:
+ ratePercents = 99;
+ break;
+ }
+
+ if (!setExifTagLong("Exif.Image.0x4749", ratePercents))
+ return false;
+
+ // Set Iptc Urgency tag value.
+
+ TQString urgencyTag;
+
+ switch(rating)
+ {
+ case 0:
+ urgencyTag = TQString("8");
+ break;
+ case 1:
+ urgencyTag = TQString("7");
+ break;
+ case 2:
+ urgencyTag = TQString("5");
+ break;
+ case 3:
+ urgencyTag = TQString("4");
+ break;
+ case 4:
+ urgencyTag = TQString("3");
+ break;
+ case 5:
+ urgencyTag = TQString("1");
+ break;
+ }
+
+ if (!setIptcTagString("Iptc.Application2.Urgency", urgencyTag))
+ return false;
+
+ return true;
+}
+
+bool DMetadata::setIptcTag(const TQString& text, int maxLength, const char* debugLabel, const char* tagKey)
+{
+ TQString truncatedText = text;
+ truncatedText.truncate(maxLength);
+ DDebug() << getFilePath() << " ==> " << debugLabel << ": " << truncatedText << endl;
+ return setIptcTagString(tagKey, truncatedText); // returns false if failed
+}
+
+bool DMetadata::setImagePhotographerId(const TQString& author, const TQString& authorTitle)
+{
+ if (!setProgramId())
+ return false;
+
+ //TODO Exernalize the hard-coded values
+ if (!setIptcTag(author, 32, "Author", "Iptc.Application2.Byline")) return false;
+ if (!setIptcTag(authorTitle, 32, "Author Title", "Iptc.Application2.BylineTitle")) return false;
+
+ return true;
+}
+
+bool DMetadata::setImageCredits(const TQString& credit, const TQString& source, const TQString& copyright)
+{
+ if (!setProgramId())
+ return false;
+
+ //TODO Exernalize the hard-coded values
+ if (!setIptcTag(credit, 32, "Credit", "Iptc.Application2.Credit")) return false;
+ if (!setIptcTag(source, 32, "Source", "Iptc.Application2.Source")) return false;
+ if (!setIptcTag(copyright, 128, "Copyright", "Iptc.Application2.Copyright")) return false;
+
+ return true;
+}
+
+bool DMetadata::setProgramId(bool on)
+{
+ if (on)
+ {
+ TQString version(digikam_version);
+ TQString software("digiKam");
+ return setImageProgramId(software, version);
+ }
+
+ return true;
+}
+
+PhotoInfoContainer DMetadata::getPhotographInformations() const
+{
+ PhotoInfoContainer photoInfo;
+
+ if (!getExif().isEmpty())
+ {
+ photoInfo.dateTime = getImageDateTime();
+ photoInfo.make = getExifTagString("Exif.Image.Make");
+ photoInfo.model = getExifTagString("Exif.Image.Model");
+
+ photoInfo.aperture = getExifTagString("Exif.Photo.FNumber");
+ if (photoInfo.aperture.isEmpty())
+ photoInfo.aperture = getExifTagString("Exif.Photo.ApertureValue");
+
+ photoInfo.exposureTime = getExifTagString("Exif.Photo.ExposureTime");
+ if (photoInfo.exposureTime.isEmpty())
+ photoInfo.exposureTime = getExifTagString("Exif.Photo.ShutterSpeedValue");
+
+ photoInfo.exposureMode = getExifTagString("Exif.Photo.ExposureMode");
+ photoInfo.exposureProgram = getExifTagString("Exif.Photo.ExposureProgram");
+
+ photoInfo.focalLength = getExifTagString("Exif.Photo.FocalLength");
+ photoInfo.focalLength35mm = getExifTagString("Exif.Photo.FocalLengthIn35mmFilm");
+
+ photoInfo.sensitivity = getExifTagString("Exif.Photo.ISOSpeedRatings");
+ if (photoInfo.sensitivity.isEmpty())
+ photoInfo.sensitivity = getExifTagString("Exif.Photo.ExposureIndex");
+
+ photoInfo.flash = getExifTagString("Exif.Photo.Flash");
+ photoInfo.whiteBalance = getExifTagString("Exif.Photo.WhiteBalance");
+ }
+
+ return photoInfo;
+}
+
+/**
+The following methods set and get an XML dataset into a private IPTC.Application2 tags
+to backup digiKam image properties. The XML text data are compressed using zlib and stored
+like a byte array. The XML text data format are like below:
+
+<?xml version="1.0" encoding="UTF-8"?>
+<digikamproperties>
+ <comments value="A cool photo from Adrien..." />
+ <date value="2006-11-23T13:36:26" />
+ <rating value="4" />
+ <tagslist>
+ <tag path="Gilles/Adrien/testphoto" />
+ <tag path="monuments/Trocadero/Tour Eiffel" />
+ <tag path="City/Paris" />
+ </tagslist>
+</digikamproperties>
+
+*/
+
+bool DMetadata::getXMLImageProperties(TQString& comments, TQDateTime& date,
+ int& rating, TQStringList& tagsPath)
+{
+ rating = 0;
+
+ TQByteArray data = getIptcTagData("Iptc.Application2.0x00ff");
+ if (data.isEmpty())
+ return false;
+ TQByteArray decompressedData = tqUncompress(data);
+ TQString doc;
+ TQDataStream ds(decompressedData, IO_ReadOnly);
+ ds >> doc;
+
+ TQDomDocument xmlDoc;
+ TQString error;
+ int row, col;
+ if (!xmlDoc.setContent(doc, true, &error, &row, &col))
+ {
+ DDebug() << doc << endl;
+ DDebug() << error << " :: row=" << row << " , col=" << col << endl;
+ return false;
+ }
+
+ TQDomElement rootElem = xmlDoc.documentElement();
+ if (rootElem.tagName() != TQString::fromLatin1("digikamproperties"))
+ return false;
+
+ for (TQDomNode node = rootElem.firstChild();
+ !node.isNull(); node = node.nextSibling())
+ {
+ TQDomElement e = node.toElement();
+ TQString name = e.tagName();
+ TQString val = e.attribute(TQString::fromLatin1("value"));
+
+ if (name == TQString::fromLatin1("comments"))
+ {
+ comments = val;
+ }
+ else if (name == TQString::fromLatin1("date"))
+ {
+ if (val.isEmpty()) continue;
+ date = TQDateTime::fromString(val, TQt::ISODate);
+ }
+ else if (name == TQString::fromLatin1("rating"))
+ {
+ if (val.isEmpty()) continue;
+ bool ok=false;
+ rating = val.toInt(&ok);
+ if (!ok) rating = 0;
+ }
+ else if (name == TQString::fromLatin1("tagslist"))
+ {
+ for (TQDomNode node2 = e.firstChild();
+ !node2.isNull(); node2 = node2.nextSibling())
+ {
+ TQDomElement e2 = node2.toElement();
+ TQString name2 = e2.tagName();
+ TQString val2 = e2.attribute(TQString::fromLatin1("path"));
+
+ if (name2 == TQString::fromLatin1("tag"))
+ {
+ if (val2.isEmpty()) continue;
+ tagsPath.append(val2);
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool DMetadata::setXMLImageProperties(const TQString& comments, const TQDateTime& date,
+ int rating, const TQStringList& tagsPath)
+{
+ TQDomDocument xmlDoc;
+
+ xmlDoc.appendChild(xmlDoc.createProcessingInstruction( TQString::fromLatin1("xml"),
+ TQString::fromLatin1("version=\"1.0\" encoding=\"UTF-8\"") ) );
+
+ TQDomElement propertiesElem = xmlDoc.createElement(TQString::fromLatin1("digikamproperties"));
+ xmlDoc.appendChild( propertiesElem );
+
+ TQDomElement c = xmlDoc.createElement(TQString::fromLatin1("comments"));
+ c.setAttribute(TQString::fromLatin1("value"), comments);
+ propertiesElem.appendChild(c);
+
+ TQDomElement d = xmlDoc.createElement(TQString::fromLatin1("date"));
+ d.setAttribute(TQString::fromLatin1("value"), date.toString(TQt::ISODate));
+ propertiesElem.appendChild(d);
+
+ TQDomElement r = xmlDoc.createElement(TQString::fromLatin1("rating"));
+ r.setAttribute(TQString::fromLatin1("value"), rating);
+ propertiesElem.appendChild(r);
+
+ TQDomElement tagsElem = xmlDoc.createElement(TQString::fromLatin1("tagslist"));
+ propertiesElem.appendChild(tagsElem);
+
+ TQStringList path = tagsPath;
+ for ( TQStringList::Iterator it = path.begin(); it != path.end(); ++it )
+ {
+ TQDomElement e = xmlDoc.createElement(TQString::fromLatin1("tag"));
+ e.setAttribute(TQString::fromLatin1("path"), *it);
+ tagsElem.appendChild(e);
+ }
+
+ TQByteArray data, compressedData;
+ TQDataStream ds(data, IO_WriteOnly);
+ ds << xmlDoc.toString();
+ compressedData = tqCompress(data);
+ return (setIptcTagData("Iptc.Application2.0x00ff", compressedData));
+}
+
+} // NameSpace Digikam