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.
532 lines
15 KiB
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 "kfile_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(kfile_jpeg, JpegFactory("kfile_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 "kfile_jpeg.moc"
|