diff options
Diffstat (limited to 'src/libs/dmetadata/dmetadata.cpp')
-rw-r--r-- | src/libs/dmetadata/dmetadata.cpp | 544 |
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 |