/* ============================================================ * * 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 * Copyright (C) 2006-2007 by Marcel Wiesweg * * 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 #include // LibKDcraw includes. #include #include // 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: */ 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