File better-exif.patch of Package kdegraphics3
--- kfile-plugins/jpeg/kfile_jpeg.cpp
+++ kfile-plugins/jpeg/kfile_jpeg.cpp
@@ -19,7 +19,6 @@
*
*/
-
#include <stdlib.h>
#include "kfile_jpeg.h"
@@ -35,8 +34,11 @@
#include <qdict.h>
#include <qvalidator.h>
#include <qimage.h>
+#include <qwmatrix.h>
-#include "exif.h"
+#include <exiv2/image.hpp>
+#include <exiv2/exif.hpp>
+#include <exiv2/tags.hpp>
#define EXIFGROUP "Jpeg EXIF Data"
@@ -58,98 +60,35 @@ KJpegPlugin::KJpegPlugin(QObject *parent
i18n("JPEG Exif") );
KFileMimeTypeInfo::ItemInfo* item;
- item = addItemInfo( exifGroup, "Comment", i18n("Comment"), QVariant::String);
+ item = addItemInfo( exifGroup, "UserComment", i18n("Comment"), QVariant::String);
setAttributes( item,
- KFileMimeTypeInfo::Modifiable |
- KFileMimeTypeInfo::Addable |
+// KFileMimeTypeInfo::Modifiable |
+// KFileMimeTypeInfo::Addable |
KFileMimeTypeInfo::MultiLine );
- item = addItemInfo( exifGroup, "Manufacturer", i18n("Camera Manufacturer"),
- QVariant::String );
-
- item = addItemInfo( exifGroup, "Model", i18n("Camera Model"),
- QVariant::String );
-
- item = addItemInfo( exifGroup, "Date/time", i18n("Date/Time"),
- QVariant::DateTime );
-
- item = addItemInfo( exifGroup, "CreationDate", i18n("Creation Date"),
- QVariant::Date );
-
- item = addItemInfo( exifGroup, "CreationTime", i18n("Creation Time"),
- QVariant::Time );
-
item = addItemInfo( exifGroup, "Dimensions", i18n("Dimensions"),
QVariant::Size );
setHint( item, KFileMimeTypeInfo::Size );
setUnit( item, KFileMimeTypeInfo::Pixels );
- item = addItemInfo( exifGroup, "Orientation", i18n("Orientation"),
- QVariant::Int );
-
- item = addItemInfo( exifGroup, "ColorMode", i18n("Color Mode"),
- QVariant::String );
-
- item = addItemInfo( exifGroup, "Flash used", i18n("Flash Used"),
- QVariant::String );
- item = addItemInfo( exifGroup, "Focal length", i18n("Focal Length"),
- QVariant::String );
- setUnit( item, KFileMimeTypeInfo::Millimeters );
-
- item = addItemInfo( exifGroup, "35mm equivalent", i18n("35mm Equivalent"),
- QVariant::Int );
- setUnit( item, KFileMimeTypeInfo::Millimeters );
-
- item = addItemInfo( exifGroup, "CCD width", i18n("CCD Width"),
- QVariant::String );
- setUnit( item, KFileMimeTypeInfo::Millimeters );
-
- item = addItemInfo( exifGroup, "Exposure time", i18n("Exposure Time"),
- QVariant::String );
- setHint( item, KFileMimeTypeInfo::Seconds );
-
- item = addItemInfo( exifGroup, "Aperture", i18n("Aperture"),
- QVariant::String );
-
- item = addItemInfo( exifGroup, "Focus dist.", i18n("Focus Dist."),
- QVariant::String );
-
- item = addItemInfo( exifGroup, "Exposure bias", i18n("Exposure Bias"),
- QVariant::String );
-
- item = addItemInfo( exifGroup, "Whitebalance", i18n("Whitebalance"),
- QVariant::String );
-
- item = addItemInfo( exifGroup, "Metering mode", i18n("Metering Mode"),
- QVariant::String );
-
- item = addItemInfo( exifGroup, "Exposure", i18n("Exposure"),
- QVariant::String );
-
- item = addItemInfo( exifGroup, "ISO equiv.", i18n("ISO Equiv."),
- QVariant::String );
-
- item = addItemInfo( exifGroup, "JPEG quality", i18n("JPEG Quality"),
- QVariant::String );
-
- item = addItemInfo( exifGroup, "User comment", i18n("User Comment"),
- QVariant::String );
- setHint(item, KFileMimeTypeInfo::Description);
-
- item = addItemInfo( exifGroup, "JPEG process", i18n("JPEG Process"),
- QVariant::String );
-
item = addItemInfo( exifGroup, "Thumbnail", i18n("Thumbnail"),
QVariant::Image );
setHint( item, KFileMimeTypeInfo::Thumbnail );
-// ###
-// exifGroup.setSupportsVariableKeys(true);
+ for (const Exiv2::TagInfo* taglist = Exiv2::ExifTags::ifdTagList();
+ taglist->tag_ != 0xffff; taglist++)
+ addItemInfo (exifGroup, taglist->name_, taglist->title_, QVariant::String);
+
+ for (const Exiv2::TagInfo* taglist = Exiv2::ExifTags::exifTagList();
+ taglist->tag_ != 0xffff; taglist++)
+ addItemInfo (exifGroup, taglist->name_, taglist->title_, QVariant::String);
+
+ //exifGroup->setSupportsVariableKeys(true);
}
QValidator* KJpegPlugin::createValidator(const KFileMetaInfoItem& /*item*/,
- QObject */*parent*/,
- const char */*name*/ ) const
+ QObject* /*parent*/,
+ const char* /*name*/ ) const
{
// no need to return a validator that validates everything as OK :)
// if (item.isEditable())
@@ -161,30 +100,48 @@ QValidator* KJpegPlugin::createValidator
bool KJpegPlugin::writeInfo( const KFileMetaInfo& info ) const
{
QString comment = info[EXIFGROUP].value("Comment").toString();
- QString path = info.path();
kdDebug(7034) << "exif writeInfo: " << info.path() << " \"" << comment << "\"\n";
+#if 0
+ try {
+ Exiv2::Image::AutoPtr image =
+ Exiv2::ImageFactory::open(QFile::encodeName(info.path()).data());
+ image->readMetadata();
+ Exiv2::ExifData &exifData = image->exifData();
+
+ /*
+ Exiv2 uses a CommentValue for Exif user comments. The format of the
+ comment string includes an optional charset specification at the beginning:
+
+ [charset=["]Ascii|Jis|Unicode|Undefined["] ]comment
+
+ Undefined is used as a default if the comment doesn't start with a charset
+ definition.
+
+ Following are a few examples of valid comments. The last one is written to
+ the file.
+ */
+ exifData["Exif.Photo.UserComment"]
+ = "charset=\"Unicode\" An Unicode Exif comment added with Exiv2";
+ exifData["Exif.Photo.UserComment"]
+ = "charset=\"Undefined\" An undefined Exif comment added with Exiv2";
+ exifData["Exif.Photo.UserComment"]
+ = "Another undefined Exif comment added with Exiv2";
+ exifData["Exif.Photo.UserComment"]
+ = "charset=Ascii An ASCII Exif comment added with Exiv2";
- /*
- Do a strictly safe insertion of the comment:
+ exifData["Exif.Photo.UserComment"]
+ = std::string("charset=\"Unicode\" ") + std::string(comment.utf8().data());
- Scan original to verify it's a proper jpeg
- Open a unique temporary file in this directory
- Write temporary, replacing all COM blocks with this one.
- Scan temporary, to verify it's a proper jpeg
- Rename original to another unique name
- Rename temporary to original
- Unlink original
- */
- /*
- The jpeg standard does not regulate the contents of the COM block.
- I'm assuming the best thing to do here is write as unicode utf-8,
- which is fully backwards compatible with readers expecting ascii.
- Readers expecting a national character set are out of luck...
- */
- if( safe_copy_and_modify( QFile::encodeName( path ), comment.utf8() ) ) {
- return false;
- }
+ image->writeMetadata();
+ }
+ catch (Exiv2::AnyError& e)
+ {
+ return false;
+ }
+#else
+ return false;
+#endif
return true;
}
@@ -194,292 +151,79 @@ bool KJpegPlugin::readInfo( KFileMetaInf
if ( path.isEmpty() ) // remote file
return false;
- QString tag;
- ExifData ImageInfo;
+ std::string tag;
- // parse the jpeg file now
try {
- if ( !ImageInfo.scan(info.path()) ) {
- kdDebug(7034) << "Not a JPEG file!\n";
+ Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(info.path().utf8().data());
+ if (!image.get())
return false;
- }
- }
- catch (FatalError& e) { // malformed exif data?
- kdDebug(7034) << "Exception caught while parsing Exif data of: " << info.path() << endl;
- e.debug_print();
- return false;
- }
+ image->readMetadata();
+ Exiv2::ExifData& ImageInfo = image->exifData();
- KFileMetaInfoGroup exifGroup = appendGroup( info, EXIFGROUP );
+ KFileMetaInfoGroup exifGroup = appendGroup( info, EXIFGROUP );
- tag = ImageInfo.getComment();
- if ( tag.length() ) {
- kdDebug(7034) << "exif inserting Comment: " << tag << "\n";
- appendItem( exifGroup, "Comment", tag );
- } else {
- appendItem( exifGroup, "Comment", tag ); // So user can add new comment
- }
-
- tag = ImageInfo.getCameraMake();
- if (tag.length())
- appendItem( exifGroup, "Manufacturer", tag );
-
- tag = ImageInfo.getCameraModel();
- if (tag.length())
- appendItem( exifGroup, "Model", tag );
-
- tag = ImageInfo.getDateTime();
- if (tag.length()){
- QDateTime dt = parseDateTime( tag.stripWhiteSpace() );
- if ( dt.isValid() ) {
- appendItem( exifGroup, "Date/time", dt );
- appendItem( exifGroup, "CreationDate", dt.date() );
- appendItem( exifGroup, "CreationTime", dt.time() );
+ tag = image->comment();
+ if ( tag.length() ) {
+ kdDebug(7034) << "exif inserting Comment: " << tag.c_str() << "\n";
+ appendItem( exifGroup, "UserComment", QString::fromUtf8(tag.c_str()) );
+ } else {
+ appendItem( exifGroup, "UserComment", QString::null); // So user can add new comment
}
- }
-
- appendItem( exifGroup,"Dimensions", QSize( ImageInfo.getWidth(),
- ImageInfo.getHeight() ) );
-
- if ( ImageInfo.getOrientation() )
- appendItem( exifGroup, "Orientation", ImageInfo.getOrientation() );
-
- appendItem( exifGroup, "ColorMode", ImageInfo.getIsColor() ?
- i18n("Color") : i18n("Black and white") );
- int flashUsed = ImageInfo.getFlashUsed(); // -1, <set>
- if ( flashUsed >= 0 ) {
- QString flash = i18n("Flash", "(unknown)");
- switch ( flashUsed ) {
- case 0: flash = i18n("Flash", "No");
- break;
- case 1:
- case 5:
- case 7:
- flash = i18n("Flash", "Fired");
- break;
- case 9:
- case 13:
- case 15:
- flash = i18n( "Flash", "Fill Fired" );
- break;
- case 16:
- flash = i18n( "Flash", "Off" );
- break;
- case 24:
- flash = i18n( "Flash", "Auto Off" );
- break;
- case 25:
- case 29:
- case 31:
- flash = i18n( "Flash", "Auto Fired" );
- break;
- case 32:
- flash = i18n( "Flash", "Not Available" );
- break;
- default:
- break;
+ for (Exiv2::ExifData::const_iterator i = ImageInfo.begin();
+ i != ImageInfo.end(); ++i) {
+ std::ostringstream str;
+ str << *i;
+ QString dt = QString::fromUtf8( str.str().c_str());
+ dt = convertDateTime( dt ); // exiv2 doesn't seem to convert to normal date/time, check
+ appendItem(exifGroup, QString::fromUtf8(i->tagName().c_str()), dt );
}
- appendItem( exifGroup, "Flash used",
- flash );
- }
-
- if (ImageInfo.getFocalLength()){
- appendItem( exifGroup, "Focal length",
- QString().sprintf("%4.1f", ImageInfo.getFocalLength()) );
-
- if (ImageInfo.getCCDWidth()){
- appendItem( exifGroup, "35mm equivalent",
- (int)(ImageInfo.getFocalLength()/ImageInfo.getCCDWidth()*35 + 0.5) );
- }
- }
- if (ImageInfo.getCCDWidth()){
- appendItem( exifGroup, "CCD width",
- QString().sprintf("%4.2f", ImageInfo.getCCDWidth()) );
- }
-
- if (ImageInfo.getExposureTime()){
- tag=QString().sprintf("%6.3f", ImageInfo.getExposureTime());
- float exposureTime = ImageInfo.getExposureTime();
- if (exposureTime > 0 && exposureTime <= 0.5){
- tag+=QString().sprintf(" (1/%d)", (int)(0.5 + 1/exposureTime) );
- }
- appendItem( exifGroup, "Exposure time", tag );
- }
-
- if (ImageInfo.getApertureFNumber()){
- appendItem( exifGroup, "Aperture",
- QString().sprintf("f/%3.1f",
- (double)ImageInfo.getApertureFNumber()));
- }
-
- if (ImageInfo.getDistance()){
- if (ImageInfo.getDistance() < 0){
- tag=i18n("Infinite");
- }else{
- tag=QString().sprintf("%5.2fm",(double)ImageInfo.getDistance());
+ Exiv2::ExifData::const_iterator iw =
+ ImageInfo.findKey(Exiv2::ExifKey("Exif.Image.ImageWidth"));
+ if (iw == ImageInfo.end())
+ iw = ImageInfo.findKey(Exiv2::ExifKey("Exif.Photo.PixelXDimension"));
+ Exiv2::ExifData::const_iterator ih =
+ ImageInfo.findKey(Exiv2::ExifKey("Exif.Image.ImageLength"));
+ if (ih == ImageInfo.end())
+ ih = ImageInfo.findKey(Exiv2::ExifKey("Exif.Photo.PixelYDimension"));
+
+ if (iw != ImageInfo.end() && ih != ImageInfo.end())
+ appendItem( exifGroup,"Dimensions", QSize( iw->toLong(), ih->toLong()));
+
+ if ( what & KFileMetaInfo::Thumbnail ) {
+ Exiv2::ExifThumbC thumb(ImageInfo); Exiv2::DataBuf thumbnail = thumb.copy();
+ QImage image;
+ image.loadFromData(thumbnail.pData_, thumbnail.size_);
+ // if there's no thumbnail in the jpeg, create a thumbnail from the file itself,
+ // so that orientation is applied correctly (kio_thumbnail doesn't handle it)
+ if( image.isNull())
+ image.load( info.path());
+ if( !image.isNull()) {
+ Exiv2::ExifData::const_iterator ort = ImageInfo.findKey(Exiv2::ExifKey("Exif.Image.Orientation"));
+ if (ort != ImageInfo.end()) {
+ QWMatrix m;
+ QWMatrix flip= QWMatrix(-1,0,0,1,0,0);
+ switch( ort->toLong()) { // notice intentional fallthroughs
+ case 2: m = flip; break;
+ case 4: m = flip;
+ case 3: m.rotate(180); break;
+ case 5: m = flip;
+ case 6: m.rotate(90); break;
+ case 7: m = flip;
+ case 8: m.rotate(270); break;
+ default: break; // should never happen
+ }
+ if( !m.isIdentity())
+ image = image.xForm( m );
+ }
+ appendItem( exifGroup, "Thumbnail", image );
+ }
}
- appendItem( exifGroup, "Focus dist.", tag );
- }
-
- if (ImageInfo.getExposureBias()){
- appendItem( exifGroup, "Exposure bias",
- QString().sprintf("%4.2f",
- (double)ImageInfo.getExposureBias()) );
}
-
- if (ImageInfo.getWhitebalance() != -1){
- switch(ImageInfo.getWhitebalance()) {
- case 0:
- tag=i18n("Unknown");
- break;
- case 1:
- tag=i18n("Daylight");
- break;
- case 2:
- tag=i18n("Fluorescent");
- break;
- case 3:
- //tag=i18n("incandescent");
- tag=i18n("Tungsten");
- break;
- case 17:
- tag=i18n("Standard light A");
- break;
- case 18:
- tag=i18n("Standard light B");
- break;
- case 19:
- tag=i18n("Standard light C");
- break;
- case 20:
- tag=i18n("D55");
- break;
- case 21:
- tag=i18n("D65");
- break;
- case 22:
- tag=i18n("D75");
- break;
- case 255:
- tag=i18n("Other");
- break;
- default:
- //23 to 254 = reserved
- tag=i18n("Unknown");
- }
- appendItem( exifGroup, "Whitebalance", tag );
- }
-
- if (ImageInfo.getMeteringMode() != -1){
- switch(ImageInfo.getMeteringMode()) {
- case 0:
- tag=i18n("Unknown");
- break;
- case 1:
- tag=i18n("Average");
- break;
- case 2:
- tag=i18n("Center weighted average");
- break;
- case 3:
- tag=i18n("Spot");
- break;
- case 4:
- tag=i18n("MultiSpot");
- break;
- case 5:
- tag=i18n("Pattern");
- break;
- case 6:
- tag=i18n("Partial");
- break;
- case 255:
- tag=i18n("Other");
- break;
- default:
- // 7 to 254 = reserved
- tag=i18n("Unknown");
- }
- appendItem( exifGroup, "Metering mode", tag );
- }
-
- if (ImageInfo.getExposureProgram()){
- switch(ImageInfo.getExposureProgram()) {
- case 0:
- tag=i18n("Not defined");
- break;
- case 1:
- tag=i18n("Manual");
- break;
- case 2:
- tag=i18n("Normal program");
- break;
- case 3:
- tag=i18n("Aperture priority");
- break;
- case 4:
- tag=i18n("Shutter priority");
- break;
- case 5:
- tag=i18n("Creative program\n(biased toward fast shutter speed)");
- break;
- case 6:
- tag=i18n("Action program\n(biased toward fast shutter speed)");
- break;
- case 7:
- tag=i18n("Portrait mode\n(for closeup photos with the background out of focus)");
- break;
- case 8:
- tag=i18n("Landscape mode\n(for landscape photos with the background in focus)");
- break;
- default:
- // 9 to 255 = reserved
- tag=i18n("Unknown");
- }
- appendItem( exifGroup, "Exposure", tag );
- }
-
- if (ImageInfo.getISOequivalent()){
- appendItem( exifGroup, "ISO equiv.",
- QString().sprintf("%2d",
- (int)ImageInfo.getISOequivalent()) );
- }
-
- if (ImageInfo.getCompressionLevel()){
- switch(ImageInfo.getCompressionLevel()) {
- case 1:
- tag=i18n("Basic");
- break;
- case 2:
- tag=i18n("Normal");
- break;
- case 4:
- tag=i18n("Fine");
- break;
- default:
- tag=i18n("Unknown");
- }
- appendItem( exifGroup, "JPEG quality", tag );
- }
-
- tag = ImageInfo.getUserComment();
- if (tag.length()){
- appendItem( exifGroup, "EXIF comment", tag );
- }
-
- int a;
- for (a=0;;a++){
- if (ProcessTable[a].Tag == ImageInfo.getProcess() || ProcessTable[a].Tag == 0){
- appendItem( exifGroup, "JPEG process",
- QString::fromUtf8( ProcessTable[a].Desc) );
- break;
- }
- }
-
- if ( what & KFileMetaInfo::Thumbnail && !ImageInfo.isNullThumbnail() ){
- appendItem( exifGroup, "Thumbnail", ImageInfo.getThumbnail() );
+ catch (Exiv2::AnyError& e) {
+ kdDebug(7034) << "Caught Exiv2 exception" << endl;
+ return false;
}
return true;
@@ -487,11 +231,10 @@ bool KJpegPlugin::readInfo( KFileMetaInf
// format of the string is:
// YYYY:MM:DD HH:MM:SS
-QDateTime KJpegPlugin::parseDateTime( const QString& string )
+QString KJpegPlugin::convertDateTime( const QString& string )
{
- QDateTime dt;
if ( string.length() != 19 )
- return dt;
+ return string;
QString year = string.left( 4 );
QString month = string.mid( 5, 2 );
@@ -521,11 +264,13 @@ QDateTime KJpegPlugin::parseDateTime( co
allOk &= ok;
if ( allOk ) {
+ QDateTime dt;
dt.setDate( QDate( y, mo, d ) );
dt.setTime( QTime( h, mi, s ) );
+ return KGlobal::locale()->formatDateTime( dt, true, true ); // short format, seconds
}
- return dt;
+ return string;
}
#include "kfile_jpeg.moc"
--- kfile-plugins/jpeg/kfile_jpeg.desktop
+++ kfile-plugins/jpeg/kfile_jpeg.desktop
@@ -60,5 +60,5 @@ Name[zu]=Ulwazi lwe-JPEG EXIF
ServiceTypes=KFilePlugin
X-KDE-Library=kfile_jpeg
MimeType=image/jpeg
-PreferredItems=User comment,CreationDate,CreationTime,Dimensions,Exposure time,JPEG quality,Comment
+PreferredItems=UserComment,Dimensions,DateTime,DateTimeOriginal,Flash,ExposureTime,Orientation,ApertureValue
SupportsThumbnail=true
--- kfile-plugins/jpeg/kfile_jpeg.h
+++ kfile-plugins/jpeg/kfile_jpeg.h
@@ -37,7 +37,7 @@ public:
QObject* parent, const char* name) const;
private:
- QDateTime parseDateTime( const QString& string );
+ QString convertDateTime( const QString& string );
};
#endif
--- kfile-plugins/jpeg/Makefile.am
+++ kfile-plugins/jpeg/Makefile.am
@@ -6,13 +6,13 @@ KDE_CXXFLAGS = $(USE_EXCEPTIONS)
INCLUDES = $(all_includes)
# these are the headers for your project
-noinst_HEADERS = kfile_jpeg.h exif.h
+noinst_HEADERS = kfile_jpeg.h
kde_module_LTLIBRARIES = kfile_jpeg.la
-kfile_jpeg_la_SOURCES = kfile_jpeg.cpp exif.cpp kfile_setcomment.cpp
+kfile_jpeg_la_SOURCES = kfile_jpeg.cpp kfile_setcomment.cpp
kfile_jpeg_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
-kfile_jpeg_la_LIBADD = $(LIB_KIO)
+kfile_jpeg_la_LIBADD = $(LIB_KIO) -lexiv2
# let automoc handle all of the meta source files (moc)
METASOURCES = AUTO