You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdegraphics/tdefile-plugins/jpeg/tdefile_jpeg.cpp

532 lines
15 KiB

/* 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"