diff options
Diffstat (limited to 'tdefile-plugins/jpeg/tdefile_jpeg.cpp')
-rw-r--r-- | tdefile-plugins/jpeg/tdefile_jpeg.cpp | 531 |
1 files changed, 531 insertions, 0 deletions
diff --git a/tdefile-plugins/jpeg/tdefile_jpeg.cpp b/tdefile-plugins/jpeg/tdefile_jpeg.cpp new file mode 100644 index 00000000..c902b99b --- /dev/null +++ b/tdefile-plugins/jpeg/tdefile_jpeg.cpp @@ -0,0 +1,531 @@ +/* This file is part of the KDE project + * Copyright (C) 2002 Frank Pieczynski <pieczy@knuut.de>, + * 2002 Carsten Pfeiffer <pfeiffer@kde.org> + * based on the jhead tool of Matthias Wandel (see below) + * + * 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 version 2. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + + +#include <stdlib.h> +#include "tdefile_jpeg.h" + +#include <kurl.h> +#include <kprocess.h> +#include <klocale.h> +#include <kgenericfactory.h> +#include <kdebug.h> + +#include <tqcstring.h> +#include <tqfile.h> +#include <tqdatetime.h> +#include <tqdict.h> +#include <tqvalidator.h> +#include <tqimage.h> + +#include "exif.h" + +#define EXIFGROUP "Jpeg EXIF Data" + +typedef KGenericFactory<KJpegPlugin> JpegFactory; + +K_EXPORT_COMPONENT_FACTORY(tdefile_jpeg, JpegFactory("tdefile_jpeg")) + +KJpegPlugin::KJpegPlugin(TQObject *parent, const char *name, + const TQStringList &args ) + : KFilePlugin(parent, name, args) +{ + kdDebug(7034) << "jpeg plugin\n"; + + // + // define all possible meta info items + // + KFileMimeTypeInfo *info = addMimeTypeInfo("image/jpeg"); + KFileMimeTypeInfo::GroupInfo *exifGroup = addGroupInfo( info, EXIFGROUP, + i18n("JPEG Exif") ); + KFileMimeTypeInfo::ItemInfo* item; + + item = addItemInfo( exifGroup, "Comment", i18n("Comment"), TQVariant::String); + setAttributes( item, + KFileMimeTypeInfo::Modifiable | + KFileMimeTypeInfo::Addable | + KFileMimeTypeInfo::MultiLine ); + + item = addItemInfo( exifGroup, "Manufacturer", i18n("Camera Manufacturer"), + TQVariant::String ); + + item = addItemInfo( exifGroup, "Model", i18n("Camera Model"), + TQVariant::String ); + + item = addItemInfo( exifGroup, "Date/time", i18n("Date/Time"), + TQVariant::DateTime ); + + item = addItemInfo( exifGroup, "CreationDate", i18n("Creation Date"), + TQVariant::Date ); + + item = addItemInfo( exifGroup, "CreationTime", i18n("Creation Time"), + TQVariant::Time ); + + item = addItemInfo( exifGroup, "Dimensions", i18n("Dimensions"), + TQVariant::Size ); + setHint( item, KFileMimeTypeInfo::Size ); + setUnit( item, KFileMimeTypeInfo::Pixels ); + + item = addItemInfo( exifGroup, "Orientation", i18n("Orientation"), + TQVariant::Int ); + + item = addItemInfo( exifGroup, "ColorMode", i18n("Color Mode"), + TQVariant::String ); + + item = addItemInfo( exifGroup, "Flash used", i18n("Flash Used"), + TQVariant::String ); + item = addItemInfo( exifGroup, "Focal length", i18n("Focal Length"), + TQVariant::String ); + setUnit( item, KFileMimeTypeInfo::Millimeters ); + + item = addItemInfo( exifGroup, "35mm equivalent", i18n("35mm Equivalent"), + TQVariant::Int ); + setUnit( item, KFileMimeTypeInfo::Millimeters ); + + item = addItemInfo( exifGroup, "CCD width", i18n("CCD Width"), + TQVariant::String ); + setUnit( item, KFileMimeTypeInfo::Millimeters ); + + item = addItemInfo( exifGroup, "Exposure time", i18n("Exposure Time"), + TQVariant::String ); + setHint( item, KFileMimeTypeInfo::Seconds ); + + item = addItemInfo( exifGroup, "Aperture", i18n("Aperture"), + TQVariant::String ); + + item = addItemInfo( exifGroup, "Focus dist.", i18n("Focus Dist."), + TQVariant::String ); + + item = addItemInfo( exifGroup, "Exposure bias", i18n("Exposure Bias"), + TQVariant::String ); + + item = addItemInfo( exifGroup, "Whitebalance", i18n("Whitebalance"), + TQVariant::String ); + + item = addItemInfo( exifGroup, "Metering mode", i18n("Metering Mode"), + TQVariant::String ); + + item = addItemInfo( exifGroup, "Exposure", i18n("Exposure"), + TQVariant::String ); + + item = addItemInfo( exifGroup, "ISO equiv.", i18n("ISO Equiv."), + TQVariant::String ); + + item = addItemInfo( exifGroup, "JPEG quality", i18n("JPEG Quality"), + TQVariant::String ); + + item = addItemInfo( exifGroup, "User comment", i18n("User Comment"), + TQVariant::String ); + setHint(item, KFileMimeTypeInfo::Description); + + item = addItemInfo( exifGroup, "JPEG process", i18n("JPEG Process"), + TQVariant::String ); + + item = addItemInfo( exifGroup, "Thumbnail", i18n("Thumbnail"), + TQVariant::Image ); + setHint( item, KFileMimeTypeInfo::Thumbnail ); + +// ### +// exifGroup.setSupportsVariableKeys(true); +} + +TQValidator* KJpegPlugin::createValidator(const KFileMetaInfoItem& /*item*/, + TQObject */*parent*/, + const char */*name*/ ) const +{ + // no need to return a validator that validates everything as OK :) +// if (item.isEditable()) +// return new TQRegExpValidator(TQRegExp(".*"), parent, name); +// else + return 0L; +} + +bool KJpegPlugin::writeInfo( const KFileMetaInfo& info ) const +{ + TQString comment = info[EXIFGROUP].value("Comment").toString(); + TQString path = info.path(); + + kdDebug(7034) << "exif writeInfo: " << info.path() << " \"" << comment << "\"\n"; + + /* + Do a strictly safe insertion of the comment: + + 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( TQFile::encodeName( path ), comment.utf8() ) ) { + return false; + } + return true; +} + +bool KJpegPlugin::readInfo( KFileMetaInfo& info, uint what ) +{ + const TQString path( info.path() ); + if ( path.isEmpty() ) // remote file + return false; + + TQString tag; + ExifData ImageInfo; + + // parse the jpeg file now + try { + if ( !ImageInfo.scan(info.path()) ) { + kdDebug(7034) << "Not a JPEG file!\n"; + 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; + } + + 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()){ + TQDateTime dt = parseDateTime( tag.stripWhiteSpace() ); + if ( dt.isValid() ) { + appendItem( exifGroup, "Date/time", dt ); + appendItem( exifGroup, "CreationDate", dt.date() ); + appendItem( exifGroup, "CreationTime", dt.time() ); + } + } + + appendItem( exifGroup,"Dimensions", TQSize( 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 ) { + TQString 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; + } + appendItem( exifGroup, "Flash used", + flash ); + } + + if (ImageInfo.getFocalLength()){ + appendItem( exifGroup, "Focal length", + TQString(TQString().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", + TQString(TQString().sprintf("%4.2f", ImageInfo.getCCDWidth()) )); + } + + if (ImageInfo.getExposureTime()){ + tag=TQString().sprintf("%6.3f", ImageInfo.getExposureTime()); + float exposureTime = ImageInfo.getExposureTime(); + if (exposureTime > 0 && exposureTime <= 0.5){ + tag+=TQString().sprintf(" (1/%d)", (int)(0.5 + 1/exposureTime) ); + } + appendItem( exifGroup, "Exposure time", tag ); + } + + if (ImageInfo.getApertureFNumber()){ + appendItem( exifGroup, "Aperture", + TQString(TQString().sprintf("f/%3.1f", + (double)ImageInfo.getApertureFNumber()))); + } + + if (ImageInfo.getDistance()){ + if (ImageInfo.getDistance() < 0){ + tag=i18n("Infinite"); + }else{ + tag=TQString().sprintf("%5.2fm",(double)ImageInfo.getDistance()); + } + appendItem( exifGroup, "Focus dist.", tag ); + } + + if (ImageInfo.getExposureBias()){ + appendItem( exifGroup, "Exposure bias", + TQString(TQString().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.", + TQString(TQString().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", + TQString::fromUtf8( ProcessTable[a].Desc) ); + break; + } + } + + if ( what & KFileMetaInfo::Thumbnail && !ImageInfo.isNullThumbnail() ){ + appendItem( exifGroup, "Thumbnail", ImageInfo.getThumbnail() ); + } + + return true; +} + +// format of the string is: +// YYYY:MM:DD HH:MM:SS +TQDateTime KJpegPlugin::parseDateTime( const TQString& string ) +{ + TQDateTime dt; + if ( string.length() != 19 ) + return dt; + + TQString year = string.left( 4 ); + TQString month = string.mid( 5, 2 ); + TQString day = string.mid( 8, 2 ); + TQString hour = string.mid( 11, 2 ); + TQString minute = string.mid( 14, 2 ); + TQString seconds = string.mid( 17, 2 ); + + bool ok; + bool allOk = true; + int y = year.toInt( &ok ); + allOk &= ok; + + int mo = month.toInt( &ok ); + allOk &= ok; + + int d = day.toInt( &ok ); + allOk &= ok; + + int h = hour.toInt( &ok ); + allOk &= ok; + + int mi = minute.toInt( &ok ); + allOk &= ok; + + int s = seconds.toInt( &ok ); + allOk &= ok; + + if ( allOk ) { + dt.setDate( TQDate( y, mo, d ) ); + dt.setTime( TQTime( h, mi, s ) ); + } + + return dt; +} + +#include "tdefile_jpeg.moc" |