harvester/common/src/harvesterexifutil.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 14 Sep 2010 22:10:25 +0300
branchRCL_3
changeset 22 29d87345eaeb
parent 21 85f623e1ef41
permissions -rw-r--r--
Revision: 201033 Kit: 201035

/*
* Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies). 
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  Exif-utilities for harvester
*
*/

#include "harvesterexifutil.h"

#include "mdsutils.h"
#include "mdeobjectdef.h"
#include "mdeconstants.h"
#include "mdeproperty.h"
#include "tz.h"
#include <ExifModify.h>
#include "OstTraceDefinitions.h"

#ifdef OST_TRACE_COMPILER_IN_USE
#include "harvesterexifutilTraces.h"
#endif



using namespace MdeConstants;

const TUint16 KIdColorSpace = 0xA001;
const TUint16 KIdResolutionUnit = 0x0128;
const TUint16 KIdYbCrPositioning = 0x0213;
const TUint16 KIdImageDescription = 0x010E;
const TUint16 KIdCopyright = 0x8298;
const TUint16 KIdUserComment = 0x9286;
const TUint16 KIdDateTime = 0x0132;
const TUint16 KIdDateTimeOriginal = 0x9003;
const TUint16 KIdDateTimeDigitized = 0x9004;
const TUint16 KIdDateTimeModified = 0x132;
const TUint16 KIdFNumber = 0x829D;
const TUint16 KIdMake = 0x010F;
const TUint16 KIdModel = 0x0110;
const TUint16 KIdFocalLength = 0x920A;
const TUint16 KIdFocalLengthIn35mm = 0xA405;
const TUint16 KIdSamplesPerPixel = 0x0115;
const TUint16 KIdISOSpeedRatings = 0x8827;
const TUint16 KIdComponentsConfig = 0x9101;
const TUint16 KIdArtist = 0x013B;
const TUint16 KIdPixelXDimension = 0xA002;
const TUint16 KIdPixelYDimension = 0xA003;
const TUint16 KIdRelatedSoundFile = 0xA004;
const TUint16 KIdFocalPlaneResolutionUnit = 0xA210;
const TUint16 KIdFocalPlaneXResolution = 0xA20E;
const TUint16 KIdFocalPlaneYResolution = 0xA20F;

const TUint16 KIdExposureTime = 0x829A;
const TUint16 KIdApertureValue = 0x9202;
const TUint16 KIdExposureBias = 0x9204;
const TUint16 KIdMeteringMode = 0x9207;
const TUint16 KIdShutterSpeed = 0x9201;
const TUint16 KIdXResolution = 0x011A;
const TUint16 KIdYResolution = 0x011B;
const TUint16 KIdWhiteBalance = 0xA403;
const TUint16 KIdExposureProgram = 0x8822;
const TUint16 KIdFlash = 0x9209;
const TUint16 KIdOrientation = 0x112;

const TUint16 KIdGpsLatitudeRef = 0x1;
const TUint16 KIdGpsLatitude = 0x2;
const TUint16 KIdGpsLongitudeRef = 0x3;
const TUint16 KIdGpsLongitude = 0x4;
const TUint16 KIdGpsAltitudeRef = 0x5;
const TUint16 KIdGpsAltitude = 0x6;
const TUint16 KIdGpsMeasureMode = 0xA;
const TUint16 KIdGpsDop = 0xB;
const TUint16 KIdGpsMapDatum = 0x12;

_LIT( KExifDateTimeFormat, "%F%Y:%M:%D %H:%T:%S\0" );
const TInt KDateBufferSize = 20;
const TInt KCoordinateBufferSize = 24;

// This is needed for exif description field
_LIT8( KAsciiCodeDesignation,     "\x41\x53\x43\x49\x49\x00\x00\x00");
_LIT8( KJisCodeDesignation,     "\x4A\x49\x53\x00\x00\x00\x00\x00");
_LIT8( KUnicodeCodeDesignation, "\x55\x4E\x49\x43\x4F\x44\x45\x00");
_LIT8( KUnknownCodeDesignation, "\x00\x00\x00\x00\x00\x00\x00\x00");

_LIT8( KNorth, "N\0" );
_LIT8( KSouth, "S\0" );
_LIT8( KEast, "E\0" );
_LIT8( KWest, "W\0" );
_LIT8( KMeasureMode2, "2\0" );
_LIT8( KMeasureMode3, "3\0" );
_LIT8( KMapDatum, "WGS-84\0");

CHarvesterExifUtil::CHarvesterExifUtil() :
    iSession( NULL ), iDefaultNamespace( NULL )
{
}


EXPORT_C CHarvesterExifUtil::~CHarvesterExifUtil()
{
}

EXPORT_C CHarvesterExifUtil* CHarvesterExifUtil::NewLC()
{
	CHarvesterExifUtil* self = new (ELeave)CHarvesterExifUtil();
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;
}

EXPORT_C CHarvesterExifUtil* CHarvesterExifUtil::NewL()
{
	CHarvesterExifUtil* self=CHarvesterExifUtil::NewLC();
	CleanupStack::Pop( self ); // self;
	return self;
}

void CHarvesterExifUtil::ConstructL()
{

}

EXPORT_C void CHarvesterExifUtil::SetSession( CMdESession* aSession )
	{
	iSession = aSession;
	if ( !iDefaultNamespace && aSession )
		{
    	TRAP_IGNORE( iDefaultNamespace = &iSession->GetDefaultNamespaceDefL() );
		}
	}


EXPORT_C TBool CHarvesterExifUtil::IsValidExifData(TPtr8 aPtr)
	{
	WRITELOG( "CHarvesterExifUtil::IsValidExifData start" );
	OstTrace0( TRACE_NORMAL, CHARVESTEREXIFUTIL_ISVALIDEXIFDATA, "CHarvesterExifUtil::IsValidExifData start" );	
	
	CExifRead* reader = NULL;
	
	TRAPD(err, reader = CExifRead::NewL(aPtr, CExifRead::ENoJpeg | CExifRead::ENoTagChecking));
	if (err != KErrNone || !reader )
		{
		WRITELOG1( "CHarvesterExifUtil::IsValidExifData - error code: %d", err );
		OstTrace1( TRACE_NORMAL, DUP1_CHARVESTEREXIFUTIL_ISVALIDEXIFDATA, "CHarvesterExifUtil::IsValidExifData -error code:%d", err );

		return EFalse;
		}
	
	delete reader;
	reader = NULL;
	
	WRITELOG( "CHarvesterExifUtil::IsValidExifData end" );	
	OstTrace0( TRACE_NORMAL, DUP2_CHARVESTEREXIFUTIL_ISVALIDEXIFDATA, "CHarvesterExifUtil::IsValidExifData end" );
	
	return ETrue;
	}

void CHarvesterExifUtil::StripNulls( HBufC& aString )
	{
	_LIT( KNull, "\0" );
	_LIT( KSpace, " " );
	
	TInt pos( 0 );
    while( (pos = aString.Find( KNull ) ) != KErrNotFound )
    	{
    	aString.Des().Replace( pos, 1, KSpace );
    	}
    aString.Des().TrimAll();
	}

HBufC16* CHarvesterExifUtil::ReadExifTagL( const CExifRead& aReader, TExifIfdType aIFD, TUint16 aTagID )
	{
	HBufC16* destination = NULL;
    if( aReader.TagExists( aTagID, aIFD ) )
    	{
        const CExifTag* tag = aReader.GetTagL( aIFD, aTagID );
    	destination = CnvUtfConverter::ConvertToUnicodeFromUtf8L( tag->Data() );
    	StripNulls( *destination );    	
    	}	
    return destination;
	}

EXPORT_C TInt CHarvesterExifUtil::ReadExifDataL( CHarvestData& aHd, CFileData& aFileData )
    {
    WRITELOG( "CHarvesterExifUtil::ReadExifDataL()" );
    OstTrace0( TRACE_NORMAL, CHARVESTEREXIFUTIL_READEXIFDATAL, "CHarvesterExifUtil::ReadExifDataL" );
    

    CExifRead* reader = CExifRead::NewL(
    		aFileData.iImageData->Des(), CExifRead::ENoJpeg | CExifRead::ENoTagChecking);
    CleanupStack::PushL(reader);
    
    // Getting description
    aHd.iDescription16 = ReadExifTagL( *reader, EIfd0, KIdImageDescription );

    // Getting UserComment
    ReadUserCommentL( aHd, reader ); 
          
    // Getting copyright
    aHd.iCopyright16 = ReadExifTagL( *reader, EIfd0, KIdCopyright );
    
    // Artist
    aHd.iArtist = ReadExifTagL( *reader, EIfd0, KIdArtist );
    
    // Getting whitebalance
    aHd.iStoreWhiteBalance = reader->GetWhiteBalance( aHd.iWhiteBalance ) == KErrNone;
 
    // Getting aHd.iFlash    
    aHd.iStoreFlash = reader->GetFlash( aHd.iFlash ) == KErrNone;
    
    // Getting exposure
    aHd.iStoreExposureProgram = reader->GetExposureProgram( aHd.iExposureProgram ) == KErrNone;
                
    // Getting width
    if ( reader->TagExists( KIdPixelXDimension, EIfdExif ) )
        {
        // PixelXDimension tag should be found in EXIF according to the standard.
        reader->GetPixelXDimension(aHd.iImageWidthExif);
        }
    
    // Getting height
    if ( reader->TagExists( KIdPixelYDimension, EIfdExif ) )
        {
        // PixelYDimension tag should be found in EXIF according to the standard.
        reader->GetPixelYDimension(aHd.iImageHeightExif);
        }
    
    // Getting aFileData.iModified date
    if ( reader->TagExists(KIdDateTime, EIfd0) )
        {
        WRITELOG( "CHarvesterExifUtil::ReadExifDataL() - getting last aFileData.iModified date (exif)" );
        OstTrace0( TRACE_NORMAL, DUP1_CHARVESTEREXIFUTIL_READEXIFDATAL, "CHarvesterExifUtil::ReadExifDataL - getting last aFileData.iModified date (exif)" );
        
        aHd.iDateModified8 = reader->GetDateTimeL();
        }
    
    // Getting original date
    if ( reader->TagExists(KIdDateTimeOriginal, EIfdExif) )
        {
        WRITELOG( "CHarvesterExifUtil::ReadExifDataL() - getting original date (exif)" );
        OstTrace0( TRACE_NORMAL, DUP2_CHARVESTEREXIFUTIL_READEXIFDATAL, "CHarvesterExifUtil::ReadExifDataL - getting original date (exif)" );
        
        aHd.iDateOriginal8 = reader->GetDateTimeOriginalL();
        }
        
    // Getting date & time digitized
    if ( reader->TagExists(KIdDateTimeDigitized, EIfdExif) )
        {
        WRITELOG( "CHarvesterExifUtil::ReadExifDataL() - getting digitized date (exif)" );
        OstTrace0( TRACE_NORMAL, DUP3_CHARVESTEREXIFUTIL_READEXIFDATAL, "CHarvesterExifUtil::ReadExifDataL - getting digitized date (exif)" );
        
        aHd.iDateDigitized8 = reader->GetDateTimeDigitizedL();
        }            
    
    //Getting camera maker
    aHd.iMake = ReadExifTagL( *reader, EIfd0, KIdMake );
    
    //Getting camera aHd.iModel
    aHd.iModel = ReadExifTagL( *reader, EIfd0, KIdModel );
    
    //Getting aHd.iOrientation
    aHd.iStoreOrientation = reader->GetOrientation( aHd.iOrientation ) == KErrNone;
    
    //Getting X Resolution
    ReadXResolutionL( aHd, reader ); 
    
    //Getting Y Resolution
    ReadXResolutionL( aHd, reader ); 
    
    //Getting resolution unit (mandatory tag)
    reader->GetResolutionUnit( aHd.iResolutionUnit );
    
    //Getting YCbCr Positioning
    aHd.iStoreYCbCrPositioning = reader->GetYCbCrPositioning( aHd.iYCbCrPositioning ) == KErrNone;
        
    //Getting exposure bias value
    ReadExposureBiasL( aHd, reader ); 
    
    //Getting exposure time
    ReadExposureTimeL( aHd, reader ); 
        
    //Getting FNumber
    ReadFNumberL( aHd, reader );
    
    //Getting Exif version
    aHd.iStoreExifVersion = reader->GetExifVersion( aHd.iExifVersion ) == KErrNone;           
        
    //Getting FlashPix version
    aHd.iStoreFlashPixVersion = reader->GetFlashPixVersion( aHd.iFlashPixVersion ) == KErrNone;
    
    // Shutter speed
    ReadShutterSpeedL( aHd, reader );
        
    //Getting aHd.iAperture
    ReadApertureValueL( aHd, reader );
    
    //Getting focal length
    ReadFocalLengthL( aHd, reader );
    
    // Getting focal length in 35 mm
    ReadFocalLength35mmL( aHd, reader );
        
    aHd.iStoreColourSpace = reader->GetColorSpace( aHd.iColourSpace ) == KErrNone;
    
    aHd.iStoreThumbCompression = reader->GetThumbnailCompression( aHd.iThumbCompression ) == KErrNone;
    
	TUint32 numerator = 0;
    TUint32 denominator = 0;
	
    //Getting thumbnail X resolution    
    TInt error = reader->GetThumbnailXResolution( numerator, denominator );
    if ( error == KErrNone )
        {
        aHd.iStoreThumbXResolution = ETrue;
        aHd.iThumbXResolution = 0.0f;
        if ( denominator > 0)
            {
            aHd.iThumbXResolution = numerator / denominator;
            }
        }
    
    //Getting thumbnail Y resolution
    error = reader->GetThumbnailYResolution( numerator, denominator );
    if ( error == KErrNone )
        {
        aHd.iStoreThumbYResolution = ETrue;
        aHd.iThumbYResolution = 0.0f;
        if ( denominator > 0 )
            {
            aHd.iThumbYResolution = numerator / denominator;
            }
        }
        
    aHd.iStoreThumbResolutionUnit =
    	reader->GetThumbnailResolutionUnit( aHd.iThumbResolutionUnit ) == KErrNone;
       
    // Bits per sample and Samples per pixel not recorded in JPEG Exif.
    if ( reader->TagExists( KIdSamplesPerPixel, EIfd0 ) )
        {
        const CExifTag* tag = reader->GetTagL( EIfd0, KIdSamplesPerPixel );
        TPtrC8 tagData = tag->Data();
        
        aHd.iSamplesPerPixel = MdsUtils::ToUInt16L( CONST_CAST( TUint8*, tagData.Ptr() ) );
        aHd.iStoreSamplesPerPixel = ETrue;
        
        WRITELOG1( "CHarvesterExifUtil::ReadExifDataL() - samples per pixel: %d", aHd.iSamplesPerPixel );
        OstTrace1( TRACE_NORMAL, DUP4_CHARVESTEREXIFUTIL_READEXIFDATAL, "CHarvesterExifUtil::ReadExifDataL - samples per pixel: %d", aHd.iSamplesPerPixel );
        
        }
    
    //Getting ISO speed rating.
    if ( reader->TagExists(KIdISOSpeedRatings, EIfdExif) )
        {
        HBufC8* iso8 = reader->GetIsoSpeedRatingsL();
        
        if ( iso8 )
            {
            aHd.iStoreIsoSpeedRating = ETrue;
            aHd.iIsoSpeedRating = iso8->Des()[0];
            delete iso8;
            iso8 = NULL;
            }    
        }
    
    //Getting components configuration
    if ( reader->TagExists( KIdComponentsConfig, EIfdExif ) )
        {
        const CExifTag* tag = reader->GetTagL(
        		EIfdExif, KIdComponentsConfig );
        TPtrC8 tagData = tag->Data();
        
        aHd.iComponentsConfiguration = MdsUtils::ToUInt32L( CONST_CAST( TUint8*, tagData.Ptr() ) );
        aHd.iStoreComponentsConfig = ETrue;
        }
    
    // Getting thumbnail compression
    aHd.iStoreThumbCompression =
    	reader->GetThumbnailCompression( aHd.iThumbCompression ) == KErrNone;
        
    // Getting metering mode
    aHd.iStoreMeteringMode = reader->GetMeteringMode( aHd.iMeteringMode ) ==KErrNone;
    
    // Getting related soundfile
    aHd.iRelatedSoundFile = ReadExifTagL( *reader, EIfdExif, KIdRelatedSoundFile );
        
    // Getting focal plane resolution unit
    if ( reader->TagExists(KIdFocalPlaneResolutionUnit, EIfdExif) )
        {
        const CExifTag* tag = reader->GetTagL(
        		EIfdExif, KIdFocalPlaneResolutionUnit );
        TPtrC8 tagData = tag->Data();
        
        aHd.iFocalPlaneResolutionUnit = MdsUtils::ToUInt16L( CONST_CAST( TUint8*, tagData.Ptr() ) );
        aHd.iStoreFocalPlaneResolutionUnit = ETrue;
        }
    
    // Getting focal plane X resolution
    ReadFocalXPlaneResolutionL( aHd, reader );

        
    // Getting focal plane Y resolution
    ReadFocalYPlaneResolutionL( aHd, reader );

    // Get GPS tags
    TBool latitudeExists = EFalse;
    
    // latitude
    ReadGPSLatitudeL( aHd, reader, latitudeExists );

    // longitude
    ReadGPSLongitudeL( aHd, reader, latitudeExists );
    
    // altitude
    ReadGPSAltitudeL( aHd, reader );
        
    CleanupStack::PopAndDestroy( reader );
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Time converting
// ---------------------------------------------------------------------------
//
EXPORT_C TTime CHarvesterExifUtil::ConvertExifDateTimeToSymbianTimeL(
		const TDesC8& aDateTime )
    {
    WRITELOG( "CHarvesterExifUtil::ConvertExifDateTimeToSymbianTimeL()" );
    OstTrace0( TRACE_NORMAL, CHARVESTEREXIFUTIL_CONVERTEXIFDATETIMETOSYMBIANTIMEL, "CHarvesterExifUtil::ConvertExifDateTimeToSymbianTimeL" );    
    
    TDateTime datetime( 0, EJanuary, 0, 0, 0, 0, 0 );
    TBuf<4> text;
    
    // Year
    TPtrC8 textPart = aDateTime.Left(4);
    TLex8 lex( textPart );
    TInt number = 0;
    TInt error = lex.Val( number );
    if ( error != KErrNone )
        {
        WRITELOG( "CHarvesterExifUtil::ConvertExifDateTimeToSymbianTimeL() - couldn't get year" );
        OstTrace0( TRACE_NORMAL, DUP1_CHARVESTEREXIFUTIL_CONVERTEXIFDATETIMETOSYMBIANTIMEL, "CHarvesterExifUtil::ConvertExifDateTimeToSymbianTimeL - couldn't get year" );
        
        User::Leave( error );
        }
    datetime.SetYear( number );
    
    // Month
    TPtrC8 textPart2 = aDateTime.Mid( 5,2 );
    lex.Assign( textPart2 );
    error = lex.Val( number );
    if ( error != KErrNone )
        {
        WRITELOG( "CHarvesterExifUtil::ConvertExifDateTimeToSymbianTimeL() - couldn't get month" );
        OstTrace0( TRACE_NORMAL, DUP2_CHARVESTEREXIFUTIL_CONVERTEXIFDATETIMETOSYMBIANTIMEL, "CHarvesterExifUtil::ConvertExifDateTimeToSymbianTimeL - couldn't get month" );
        
        User::Leave( error );
        }        
    number--;
    TMonth month = (TMonth) number;
    datetime.SetMonth( month );
    
    // Day
    TPtrC8 textPart3 = aDateTime.Mid( 8,2 );
    lex.Assign( textPart3 );
    error = lex.Val( number );
    if ( error != KErrNone )
        {
        WRITELOG( "CHarvesterExifUtil::ConvertExifDateTimeToSymbianTimeL() - couldn't get date" );
        OstTrace0( TRACE_NORMAL, DUP3_CHARVESTEREXIFUTIL_CONVERTEXIFDATETIMETOSYMBIANTIMEL, "CHarvesterExifUtil::ConvertExifDateTimeToSymbianTimeL - couldn't get date" );
        
        User::Leave( error );
        }
    datetime.SetDay( number - 1 );
    
    // Hours
    TPtrC8 textPart4 = aDateTime.Mid( 11,2 );
    lex.Assign( textPart4 );
    error = lex.Val( number );
    if ( error != KErrNone )
        {
        WRITELOG( "CHarvesterExifUtil::ConvertExifDateTimeToSymbianTimeL() - couldn't get hours" );
        OstTrace0( TRACE_NORMAL, DUP4_CHARVESTEREXIFUTIL_CONVERTEXIFDATETIMETOSYMBIANTIMEL, "CHarvesterExifUtil::ConvertExifDateTimeToSymbianTimeL - couldn't get hours" );
        
        User::Leave( error );
        }    
    datetime.SetHour( number );
    
    // Minutes
    TPtrC8 textPart5 = aDateTime.Mid( 14,2 );
    lex.Assign( textPart5 );
    error = lex.Val( number );
    if ( error != KErrNone )
        {
        WRITELOG( "CHarvesterExifUtil::ConvertExifDateTimeToSymbianTimeL() - couldn't get minutes" );
        OstTrace0( TRACE_NORMAL, DUP5_CHARVESTEREXIFUTIL_CONVERTEXIFDATETIMETOSYMBIANTIMEL, "CHarvesterExifUtil::ConvertExifDateTimeToSymbianTimeL - couldn't get minutes" );
        
        User::Leave( error );
        }
    datetime.SetMinute( number );
    
    // Seconds
    TPtrC8 textPart6 = aDateTime.Mid( 17,2 );
    lex.Assign( textPart6 );
    error = lex.Val( number );
    if ( error != KErrNone )
        {
        WRITELOG( "CHarvesterExifUtil::ConvertExifDateTimeToSymbianTimeL() - couldn't get seconds" );
        OstTrace0( TRACE_NORMAL, DUP6_CHARVESTEREXIFUTIL_CONVERTEXIFDATETIMETOSYMBIANTIMEL, "CHarvesterExifUtil::ConvertExifDateTimeToSymbianTimeL - couldn't get seconds" );
        
        User::Leave( error );
        }
    datetime.SetSecond( number );
    
    TTime time( datetime );

    return time;
    }

void CHarvesterExifUtil::AddPropertyL( CMdEObjectDef& aObjectDef, CMdEObject& aMdeObject,
		const TDesC& aProperty, TUint16 aValue )
	{
	CMdEPropertyDef& propDef = aObjectDef.GetPropertyDefL( aProperty );    
	CMdEProperty* mdeProp = NULL;
	
	aMdeObject.Property( propDef, mdeProp, 0 );
    if ( !mdeProp )
        {
        aMdeObject.AddUint16PropertyL( propDef, aValue );
        }	
	}

void CHarvesterExifUtil::AddPropertyL( CMdEObjectDef& aObjectDef, CMdEObject& aMdeObject,
		   const TDesC& aProperty, TUint32 aValue )
{
	CMdEPropertyDef& propDef = aObjectDef.GetPropertyDefL( aProperty );    
	CMdEProperty* mdeProp = NULL;
	
	aMdeObject.Property( propDef, mdeProp, 0 );
	if ( !mdeProp )
		{
		aMdeObject.AddUint32PropertyL( propDef, aValue );
		}	
}

void CHarvesterExifUtil::SetExifDefaultsL( CMdEObject& aMdeObject, CExifModify& aExifModify )
	{
    const TUint32 KPixPerResolution = 72;
    const TUint32 KPixPerResDenm = 1;
    const TUint16 KResUnitInch = 2;
    const TUint16 KYCbCrPositioning = 1;
    const TUint8  KCompConf1st = 1;
    const TUint8  KCompConf2nd = 2;
    const TUint8  KCompConf3rd = 3;
    const TUint8  KCompConf4rd = 0;
    const TUint16 KColorSpaceRGB = 1;

    CMdEObjectDef& imageDef = iDefaultNamespace->GetObjectDefL( Image::KImageObject );
    
    aExifModify.SetXResolutionL( KPixPerResolution, KPixPerResDenm );
    aExifModify.SetYResolutionL( KPixPerResolution, KPixPerResDenm );

    AddPropertyL( imageDef, aMdeObject, MediaObject::KResolutionUnitProperty,
    		      KResUnitInch );
    aExifModify.SetResolutionUnitL( KResUnitInch );

    AddPropertyL( imageDef, aMdeObject, Image::KYCbCrPositioningProperty,
    		      KYCbCrPositioning );
    aExifModify.SetYCbCrPositioningL( KYCbCrPositioning );

    TUint32 compUint32( 0 );
    TUint8* components = (TUint8*) &compUint32;
    *(components + 3) = KCompConf4rd;
    *(components + 2) = KCompConf3rd;
    *(components + 1) = KCompConf2nd;
          *components = KCompConf1st;
    
    AddPropertyL( imageDef, aMdeObject, Image::KComponentsConfigurationProperty,
    		      compUint32 );
    aExifModify.SetComponentsConfigurationL( KCompConf1st, KCompConf2nd,
    										 KCompConf3rd, KCompConf4rd );

    AddPropertyL( imageDef, aMdeObject, Image::KColourSpaceProperty, KColorSpaceRGB );
    aExifModify.SetColorSpaceL( KColorSpaceRGB );
	}

HBufC8* CHarvesterExifUtil::GetPropertyValueLC( const CMdEPropertyDef& aPropDef,
		const CMdEProperty& aProperty )
	{
	switch( aPropDef.PropertyType() )
		{
		case EPropertyReal32:
			{
			TUint32 denominator = 1; 
			if( aPropDef.Name().CompareF( Image::KExposureTimeProperty ) == 0 )
				{
				denominator = 1000000;
				}
			else if( aPropDef.Name().CompareF( Image::KApertureValueProperty ) == 0 )
				{
				denominator = 100;
				}
			else if( aPropDef.Name().CompareF( Image::KExposureBiasValueProperty ) == 0 )
				{
				denominator = 100;
				}
			else if( aPropDef.Name().CompareF( Image::KShutterSpeedValueProperty ) == 0 )
				{
				denominator = 1000;
				}
			else if( aPropDef.Name().CompareF( Image::KFNumberProperty ) == 0 )
				{
				denominator = 10;
				}			
			TUint32 value = TUint32( aProperty.Real32ValueL() * (TReal32)denominator );

			HBufC8* buf8 = HBufC8::NewLC( 2 * sizeof(TUint32) );
			TPtr8 ptr = buf8->Des();
			ptr.Append( (TUint8*)&value, sizeof(TUint32) );
			ptr.Append( (TUint8*)&denominator, sizeof(TUint32) );
			
			return buf8;
			}
		case EPropertyTime:
			{
	        TTime time = aProperty.TimeValueL();
	        if( aPropDef.Name().CompareF( Image::KDateTimeProperty ) == 0 )
	        	{
	        	RTz timezone;
	        	CleanupClosePushL( timezone );
	        	User::LeaveIfError( timezone.Connect() );
	        	timezone.ConvertToLocalTime( time );
	        	CleanupStack::PopAndDestroy( &timezone );
	        	}
	        HBufC* buf = HBufC::NewLC( KDateBufferSize );
	        TPtr ptr = buf->Des();
	        time.FormatL( ptr, KExifDateTimeFormat );
	        HBufC8* buf8 = CnvUtfConverter::ConvertFromUnicodeToUtf8L( ptr );
	        CleanupStack::PopAndDestroy( buf );
	        CleanupStack::PushL( buf8 );
	        return buf8;
			}
		case EPropertyText:
			{
			TPtrC text = aProperty.TextValueL();
			if( aPropDef.Name().CompareF( MediaObject::KCommentProperty ) == 0 )
				{
		        const TUint16 bufLength = KUnicodeCodeDesignation.iTypeLength + text.Size();
		        HBufC8* commentBuf = HBufC8::NewLC( bufLength );
		        TPtr8 commentPtr = commentBuf->Des();
		        commentPtr.Append( KUnicodeCodeDesignation );
		        commentPtr.Append( (TUint8*)text.Ptr(), text.Size() );
		        return commentBuf;
				}
			HBufC8* buf = CnvUtfConverter::ConvertFromUnicodeToUtf8L( text );
			CleanupStack::PushL( buf );
	        return buf;
			}
		case EPropertyUint16:
			{
        	TUint16 value = aProperty.Uint16ValueL();
        	HBufC8* buf = HBufC8::NewLC( sizeof(value) );
        	TPtr8 ptr = buf->Des();
        	ptr.Copy( (TUint8*)(&value), sizeof(value) );
			return buf;
			}
		default:
			User::Leave( KErrGeneral );
		}
	    return NULL;
	}

CExifTag::TExifTagDataType CHarvesterExifUtil::ExifTagDataType( TUint16 aTagID, const CMdEPropertyDef& aPropDef )
	{
	if( aTagID == KIdUserComment )
		{
		return CExifTag::ETagUndefined;   		
		}
	if( aTagID == KIdShutterSpeed || aTagID == KIdExposureBias )
		{
		return CExifTag::ETagSrational;
		}
	switch( aPropDef.PropertyType() )
		{
		case EPropertyBool:
		case EPropertyInt8:
		case EPropertyUint8:
			return CExifTag::ETagByte;
		case EPropertyInt16:
		case EPropertyUint16:
			return CExifTag::ETagShort;
		case EPropertyUint32:
			return CExifTag::ETagLong;
		case EPropertyInt32:
			return CExifTag::ETagSlong;
		case EPropertyReal32:
			return CExifTag::ETagRational;
		case EPropertyTime:
			return CExifTag::ETagAscii;
		case EPropertyText:
			return CExifTag::ETagAscii;
		default:
			return CExifTag::ETagUndefined;
		}
	}

TBool CHarvesterExifUtil::CompareTag( TPtrC8 aMdeData, const CExifTag* aTag )
	{
	if( aTag->TagInfo().iDataType == CExifTag::ETagRational )
		{
		TUint32 denominator;
		TUint32 value;
        TPtrC8 ptr( aTag->Data() );
        memcpy( &value, ptr.Ptr(), sizeof(value) );
        memcpy( &denominator, ptr.Ptr()+sizeof(value), sizeof(denominator) );

    	TReal32 tagValue = 0.0f;
    	if ( denominator != 0 )
    		{
    		tagValue = (TReal32)value / (TReal32)denominator;
    		}
        ptr.Set( aMdeData );
        memcpy( &value, ptr.Ptr(), sizeof(value) );
        memcpy( &denominator, ptr.Ptr()+sizeof(value), sizeof(denominator) );

    	TReal32 mdeValue = 0.0f;
    	if ( denominator != 0 )
    		{
    		mdeValue = (TReal32)value / (TReal32)denominator;
    		}
    	return Abs( tagValue - mdeValue ) > 0.01f;
		}
	else
		{
		return aMdeData.CompareF( aTag->Data() ) != 0; 
		}
	}

TBool CHarvesterExifUtil::ModifyExifTagL( CMdEObject& aMdeObject, CExifModify& aExifModify,
		const TDesC& aProperty, TExifIfdType aIFD, TUint16 aTagID, TBool aRemove )
	{
	CMdEObjectDef& imageDef = iDefaultNamespace->GetObjectDefL( Image::KImageObject );
	CMdEPropertyDef& propDef = imageDef.GetPropertyDefL( aProperty );    
	CMdEProperty* mdeProp = NULL;
	TBool exifChanged = EFalse;
	
	aMdeObject.Property( propDef, mdeProp, 0 );
	TBool tagExists = EFalse;
	if( aRemove )
		{
		tagExists = aExifModify.Reader()->TagExists( aTagID, aIFD );
		}
	if ( mdeProp )
        {
        HBufC8* mdedata = GetPropertyValueLC( propDef, *mdeProp );
        TPtrC8 ptr = mdedata->Des();
        TBool change = EFalse;
        const CExifTag* tag = NULL;
        TRAP_IGNORE( tag = aExifModify.Reader()->GetTagL( aIFD, aTagID ) );
        if( !tag ) //create new exif tag
        	{
        	change = ETrue;
        	}
        else if( CompareTag( ptr, tag ) )
            {
            change = ETrue;
            }
        if( change )
        	{
        	CExifTag::TExifTagDataType type = ExifTagDataType( aTagID, propDef );
        	TInt len = 1;
        	if( type == CExifTag::ETagUndefined || type == CExifTag::ETagAscii  )
        		{
        		len = ptr.Length();
        		}
        	TExifTagInfo info( aTagID, type, len  );
            aExifModify.SetTagL( aIFD, info, ptr );
            exifChanged = ETrue;        	
        	}
        CleanupStack::PopAndDestroy( mdedata );
        }
	else if( tagExists ) // remove from exif
        {
        aExifModify.DeleteTag( aIFD, aTagID );
        exifChanged = ETrue;
        }
    return exifChanged;
	}

// ---------------------------------------------------------------------------
// ComposeExifData
// ---------------------------------------------------------------------------
//
EXPORT_C TInt CHarvesterExifUtil::ComposeExifDataL( CMdEObject& aMdeObject, TPtr8 aImagePtr, HBufC8*& aModified )
	{

	if ( !iSession )
		{
		User::Leave( KErrSessionClosed );
		}
	
    WRITELOG1( "CHarvesterExifUtil::ComposeExifData() - Compose Start Object ID: %d", aMdeObject.Id() );
    TBool exifChanged = EFalse;

    // 2. try to init EXIF data from image file's data
    CExifModify* modifyExif = NULL;
    TInt exifError = KErrNone;
    TRAP( exifError, modifyExif = CExifModify::NewL( aImagePtr, 
    		CExifModify::EModify, CExifModify::ENoJpegParsing ) );

    // 3. Is this image format supported?
    if ( exifError == KErrNotSupported )
        {
        WRITELOG( "CHarvesterExifUtil::ComposeExifData() - Image format not supported (!jpeg)" );
        User::Leave( exifError );
        }

    // 4. if image does not contain EXIF data try to create it
    if ( exifError != KErrNone )
        {
        WRITELOG( "CHarvesterExifUtil::ComposeExifData() - Image doesn't contain EXIF data" );
        modifyExif = CExifModify::NewL( aImagePtr, 
        		CExifModify::ECreate, CExifModify::ENoJpegParsing );
        CleanupStack::PushL( modifyExif );
        SetExifDefaultsL( aMdeObject, *modifyExif );
        CleanupStack::Pop( modifyExif );
        exifChanged = ETrue;
        }
    CleanupStack::PushL( modifyExif );
    const CExifRead* readExif = modifyExif->Reader();
    CMdEObjectDef& imageDef = iDefaultNamespace->GetObjectDefL( Image::KImageObject );
    
    // Set pixel X dimension tag (mandatory)
    TBool changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		MediaObject::KWidthProperty, EIfdExif, KIdPixelXDimension);
    exifChanged = (exifChanged | changed);

    // Set pixel Y dimension tag (mandatory)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		MediaObject::KHeightProperty, EIfdExif, KIdPixelYDimension);
    exifChanged = (exifChanged | changed);

    // Set white balance tag (recommended)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		Image::KWhiteBalanceProperty, EIfdExif, KIdWhiteBalance, ETrue );
    exifChanged = (exifChanged | changed);
    
    // Set flash tag (recommended)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		Image::KFlashProperty, EIfdExif, KIdFlash, ETrue );
    exifChanged = (exifChanged | changed);
    
    // Set exposure program tag (optional)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		Image::KExposureProgramProperty, EIfdExif, KIdExposureProgram, ETrue );
    exifChanged = (exifChanged | changed);

    // Set description tag (recommended)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		MediaObject::KDescriptionProperty, EIfd0, KIdImageDescription, ETrue );
    exifChanged = (exifChanged | changed);
    
    // Set user comment tag (optional)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		MediaObject::KCommentProperty, EIfdExif, KIdUserComment, ETrue );
    exifChanged = (exifChanged | changed);
    
    // Set copyright tag (optional)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		MediaObject::KCopyrightProperty, EIfd0, KIdCopyright, ETrue );
    exifChanged = (exifChanged | changed);

    // Set DateTimeOriginal tag (optional)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		Image::KDateTimeOriginalProperty, EIfdExif, KIdDateTimeOriginal, ETrue );
    exifChanged = (exifChanged | changed);
    
    // Set DateTimeDigitized tag (optional)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		Image::KDateTimeDigitizedProperty, EIfdExif, KIdDateTimeDigitized, ETrue );
    exifChanged = (exifChanged | changed);
    
    // Set DateTime (_image_ modified) tag (recommended)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		Image::KDateTimeProperty, EIfd0, KIdDateTimeModified, ETrue );
    exifChanged = (exifChanged | changed);
    
    // Set maker tag (optional)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		Image::KMakeProperty, EIfd1, KIdMake, ETrue );
    exifChanged = (exifChanged | changed);
    
    // Set model tag (optional)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		Image::KModelProperty, EIfd1, KIdModel, ETrue );
    exifChanged = (exifChanged | changed);
    
    // Set orientation tag (recommended)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		Image::KOrientationProperty, EIfd0, KIdOrientation , ETrue );
    exifChanged = (exifChanged | changed);

    // Set YCbCrPositioning tag (mandatory)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		Image::KYCbCrPositioningProperty, EIfd1, KIdYbCrPositioning, EFalse );
    exifChanged = (exifChanged | changed);
    
    // Set resolution unit tag (mandatory)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		MediaObject::KResolutionUnitProperty, EIfd1, KIdResolutionUnit, EFalse );
    exifChanged = (exifChanged | changed);
    
    // Set ISO speed tag (optional)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		Image::KISOSpeedRatingsProperty, EIfdExif, KIdISOSpeedRatings, ETrue );
    exifChanged = (exifChanged | changed);

    // Set related soundfile tag (optional)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		Image::KRelatedSoundFileProperty, EIfdExif, KIdRelatedSoundFile, ETrue );
    exifChanged = (exifChanged | changed);    
    
    // Set exposure time tag (recommended)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		Image::KExposureTimeProperty, EIfdExif, KIdExposureTime, ETrue );
    exifChanged = (exifChanged | changed);   
        
    // Set aperture value tag (optional)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		Image::KApertureValueProperty, EIfdExif, KIdApertureValue, ETrue );
    exifChanged = (exifChanged | changed);  
    
    // Set colour space tag (mandatory)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
		Image::KColourSpaceProperty, EIfdExif, KIdColorSpace , EFalse );
    exifChanged = (exifChanged | changed);
        
    // Set exposure bias tag (optional)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		Image::KExposureBiasValueProperty, EIfdExif, KIdExposureBias, ETrue );
    exifChanged = (exifChanged | changed);
    
    // Set metering mode tag (optional)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		Image::KMeteringModeProperty, EIfdExif, KIdMeteringMode, ETrue );
    exifChanged = (exifChanged | changed);

    // Set shutter speed tag (optional)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		Image::KShutterSpeedValueProperty, EIfdExif, KIdShutterSpeed, ETrue );
    exifChanged = (exifChanged | changed);

    // Set X resolution tag (mandatory)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		Image::KXResolutionProperty, EIfd0, KIdXResolution, EFalse );
    exifChanged = (exifChanged | changed); 

    // Set Y resolution tag (mandatory)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		Image::KYResolutionProperty, EIfd0, KIdYResolution, EFalse );
    exifChanged = (exifChanged | changed); 
    
    // Set F number tag (optional)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		Image::KFNumberProperty, EIfdExif, KIdFNumber, ETrue );
    exifChanged = (exifChanged | changed);     
    
    // Set focal length tag (optional)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		Image::KFocalLengthProperty, EIfdExif, KIdFocalLength, ETrue );
    exifChanged = (exifChanged | changed);  
    
    // Set focal length in 35 mm film tag (optional)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		Image::KFocalLengthIn35mmFilmProperty, EIfdExif, KIdFocalLengthIn35mm, ETrue );
    exifChanged = (exifChanged | changed);   

    // Set focal plane resolution unit (optional)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		Image::KFocalPlaneResolutionUnitProperty, EIfdExif, KIdFocalPlaneResolutionUnit, ETrue );
    exifChanged = (exifChanged | changed);   

    // Set focal plane X resolution (optional)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		Image::KFocalPlaneXResolutionProperty, EIfdExif, KIdFocalPlaneXResolution, ETrue );
    exifChanged = (exifChanged | changed);
    
    // Set focal plane Y resolution (optional)
    changed = ModifyExifTagL( aMdeObject, *modifyExif,
    		Image::KFocalPlaneYResolutionProperty, EIfdExif, KIdFocalPlaneYResolution, ETrue );
    exifChanged = (exifChanged | changed);
    
    TUint16 uint16Value( 0 );
    TUint32 uint32Value( 0 );
    
    // Set components configuration tag (mandatory)
    const CMdEPropertyDef& componentsDef = imageDef.GetPropertyDefL(
     		Image::KComponentsConfigurationProperty );
        {
        CMdEProperty* componentsProp = NULL;
        aMdeObject.Property( componentsDef, componentsProp, 0 );

        if ( componentsProp )
            {
            TUint32 componentsValue = componentsProp->Uint32ValueL();
            TUint8* components = (TUint8*) &componentsValue;
            const TUint8 KComponent4th = *(components + 3);
            const TUint8 KComponent3rd = *(components + 2);
            const TUint8 KComponent2nd = *(components + 1);
            const TUint8 KComponent1st = *components;
            
            TUint8 exifComponent4th( 0 );
            TUint8 exifComponent3rd( 0 );
            TUint8 exifComponent2nd( 0 );
            TUint8 exifComponent1st( 0 );
            
            exifError = readExif->GetComponentsConfiguration(
              	exifComponent1st, exifComponent2nd, exifComponent3rd, exifComponent4th );
            if ( exifError != KErrNone || 
                 exifComponent1st != KComponent1st || exifComponent2nd != KComponent2nd ||
                 exifComponent3rd != KComponent3rd || exifComponent4th != KComponent4th )
                {
                modifyExif->SetComponentsConfigurationL(
                		KComponent1st, KComponent2nd, KComponent3rd, KComponent4th );
                exifChanged = ETrue;
                }
            }
        }        
        
	// Set thumbnail X resolution tag (mandatory)
    const CMdEPropertyDef& thumbXDef = imageDef.GetPropertyDefL(
        Image::KThumbXResolutionProperty );
        {
        CMdEProperty* thumbXProp = NULL;
        aMdeObject.Property( thumbXDef, thumbXProp, 0 );

        if ( thumbXProp )
            {
            const TUint32 thumbX = thumbXProp->Uint32ValueL();

            TUint32 exifDenominator = 0;
            exifError = readExif->GetThumbnailXResolution( uint32Value, exifDenominator );
            TUint32 exifThumbXResol = 0;
            if ( exifDenominator > 0 )
                {
                exifThumbXResol = uint32Value / exifDenominator;
                }

            if ( exifError != KErrNone || exifThumbXResol != thumbX )
                {
                const TUint32 KDenominator = 1;
                modifyExif->SetThumbnailXResolutionL( thumbX, KDenominator );
                exifChanged = ETrue;
                }
            }
        }

	// Set thumbnail Y resolution tag (mandatory)
    const CMdEPropertyDef& thumbYDef = imageDef.GetPropertyDefL(
      		Image::KThumbYResolutionProperty );
        {
        CMdEProperty* thumbYProp = NULL;
        aMdeObject.Property( thumbYDef, thumbYProp, 0 );

        if ( thumbYProp )
            {
            TUint32 thumbY = TUint32( thumbYProp->Uint32ValueL() );
            TUint32 exifDenominator = 0;
            exifError = readExif->GetThumbnailYResolution( uint32Value, exifDenominator );
            TUint32 exifThumbYResol = 0;
            if ( exifDenominator > 0 )
                {
                exifThumbYResol = uint32Value / exifDenominator;
                }
            
            if ( exifError != KErrNone || exifThumbYResol != thumbY )
                {
                const TUint32 KDenominator = 1;
                modifyExif->SetThumbnailYResolutionL( thumbY, KDenominator );
                exifChanged = ETrue;
                }
            }
        }

	// Set thumbnail resolution unit tag (mandatory)
    const CMdEPropertyDef& thumbResolutionUnitDef = imageDef.GetPropertyDefL(
        	Image::KThumbResolutionUnitProperty );
        {
        CMdEProperty* thumbResolutionUnitProp = NULL;            
        aMdeObject.Property( thumbResolutionUnitDef, thumbResolutionUnitProp, 0 );

        if ( thumbResolutionUnitProp )
            {
            exifError = readExif->GetThumbnailResolutionUnit( uint16Value );
            const TUint16 thumbnailResolutionUnitValue =
             	thumbResolutionUnitProp->Uint16ValueL();
            if ( exifError != KErrNone || uint16Value != thumbnailResolutionUnitValue )
                {
                modifyExif->SetThumbnailResolutionUnitL( thumbnailResolutionUnitValue );
                exifChanged = ETrue;
                }
            }
        }
        
    if ( exifChanged )
        {
        WRITELOG( "CHarvesterExifUtil::ComposeExifData() - write exif to buffer" );
        aModified = modifyExif->WriteDataL( aImagePtr );
        }
    CleanupStack::PopAndDestroy( modifyExif );

    WRITELOG1( "CHarvesterExifUtil::ComposeExifData() - Compose End Object ID: %d", aMdeObject.Id() );

	return KErrNone;
	}

// ---------------------------------------------------------------------------
// ComposeLocation
// ---------------------------------------------------------------------------
//
EXPORT_C void CHarvesterExifUtil::ComposeLocationL( CMdEObject* aLocation, TPtr8 aImagePtr, HBufC8*& aModified )
    {
    CMdEProperty* latitudeProperty = NULL;
    CMdEProperty* longitudeProperty = NULL;
    CMdEProperty* altitudeProperty = NULL;
    CMdEProperty* qualityProperty = NULL;
    
    CMdEObjectDef& locationDef = iDefaultNamespace->GetObjectDefL( Location::KLocationObject );

    aLocation->Property( locationDef.GetPropertyDefL(
    		Location::KLatitudeProperty ), latitudeProperty, 0 );
    aLocation->Property( locationDef.GetPropertyDefL(
    		Location::KLongitudeProperty ), longitudeProperty, 0 );
    aLocation->Property( locationDef.GetPropertyDefL(
    		Location::KAltitudeProperty ), altitudeProperty, 0 );
    aLocation->Property( locationDef.GetPropertyDefL(
    		Location::KQualityProperty ), qualityProperty, 0 );

    CExifModify* exifWriter = CExifModify::NewL( aImagePtr, 
    		CExifModify::EModify, CExifModify::ENoJpegParsing );
    CleanupStack::PushL( exifWriter );
    const CExifRead* exifReader = exifWriter->Reader();

    TBool exifChanged = EFalse;
    TBool changed = EFalse;
    const TReal KAngleSecond = 1.0 / 3600.0;
    TInt exifError( 0 );
    // location data (all fields are optional)
    // latitude
    const CExifTag* exifLatitudeRefTag = NULL;
    TRAP( exifError, exifLatitudeRefTag = exifReader->GetTagL( EIfdGps, KIdGpsLatitudeRef ) );
    if ( latitudeProperty )
        {
        TBuf8<2> south( KSouth );
        TBuf8<2> north( KNorth );
        TReal64 latitude = latitudeProperty->Real64ValueL();

        TReal64 exifLatitude( 0.0 );
        TBuf8<2> exifLatitudeRef;
        if ( exifError == KErrNone )
            {
            TPtrC8 exifLatitudeRefBuf( exifLatitudeRefTag->Data() );
            exifLatitudeRef.Append( exifLatitudeRefBuf.Ptr(), 2 );

            if ( latitude < 0 )
                {
                if ( south.Compare(exifLatitudeRef) )
                    {
                    changed = ETrue;
                    }
                }
            else
                {
                if ( north.Compare(exifLatitudeRef) )
                    {
                    changed = ETrue;
                    }
                }

            if ( !changed )
                {
                TBuf8<KCoordinateBufferSize> exifLatitudeBuf;
                const CExifTag* exifLatitudeTag = NULL;
                TRAPD( err, exifLatitudeTag = exifReader->GetTagL( EIfdGps, KIdGpsLatitude ) );
                if ( err == KErrNone )
                    {
                    TPtrC8 exifLatitudeTagBuf( exifLatitudeTag->Data() );
                    exifLatitudeBuf.Append( exifLatitudeTagBuf.Ptr(), KCoordinateBufferSize );
                    MdsUtils::ConvertFromDegreesToDecimalL( exifLatitudeBuf, exifLatitude );
                    }
                else
                    {
                    exifError = err;
                    }
                }
            }

        if ( exifError != KErrNone || changed || Abs(exifLatitude - latitude) > KAngleSecond )
            {
            // latitude ref (N/S)
            if ( latitude < 0 )
                {
                exifWriter->SetTagL( EIfdGps, TExifTagInfo(
                		KIdGpsLatitudeRef, CExifTag::ETagAscii, 2 ), south );
                latitude = -latitude;
                }
            else
                {
                exifWriter->SetTagL( EIfdGps, TExifTagInfo(
                		KIdGpsLatitudeRef, CExifTag::ETagAscii, 2 ), north );
                }

            TBuf8<KCoordinateBufferSize> latitudeBuf;
            MdsUtils::ConvertFromDecimalToDegreesL( latitude, latitudeBuf );
            exifWriter->SetTagL( EIfdGps, TExifTagInfo(
            		KIdGpsLatitude, CExifTag::ETagRational, 3 ), latitudeBuf );
            exifChanged = ETrue;
            }
        }
    else if ( exifError == KErrNone )
        {
        exifWriter->DeleteTag( EIfdGps, KIdGpsLatitudeRef );
        exifWriter->DeleteTag( EIfdGps, KIdGpsLatitude );
        exifChanged = ETrue;
        }

    changed = EFalse;
    
    // longitude
    const CExifTag* exifLongitudeRefTag = NULL;
    TRAP( exifError, exifLongitudeRefTag = exifReader->GetTagL(
    		EIfdGps, KIdGpsLongitudeRef ) );
    if ( longitudeProperty )
        {
        TBuf8<2> west( KWest );
        TBuf8<2> east( KEast );        
        TReal64 longitude = longitudeProperty->Real64ValueL();
        
        TReal64 exifLongitude( 0.0 );
        TBuf8<2> exifLongitudeRef;
        if ( exifError == KErrNone )
            {
            TPtrC8 exifLongitudeRefBuf( exifLongitudeRefTag->Data() );
            exifLongitudeRef.Append( exifLongitudeRefBuf.Ptr(), 2 );

            if ( longitude < 0 )
                {
                if ( west.Compare(exifLongitudeRef) )
                    {
                    changed = ETrue;
                    }
                }
            else
                {
                if ( east.Compare(exifLongitudeRef) )
                    {
                    changed = ETrue;
                    }
                }

            if ( !changed )
                {
                TBuf8<KCoordinateBufferSize> exifLongitudeBuf;
                const CExifTag* exifLongitudeTag = NULL;
                TRAPD( err, exifLongitudeTag = exifReader->GetTagL( EIfdGps, KIdGpsLongitude ) );
                if ( err == KErrNone )
                    {
                    TPtrC8 exifLongitudeTagBuf( exifLongitudeTag->Data() );
                    exifLongitudeBuf.Append( exifLongitudeTagBuf.Ptr(), KCoordinateBufferSize );
                    MdsUtils::ConvertFromDegreesToDecimalL( exifLongitudeBuf, exifLongitude );
                    if ( exifLongitudeRef.Compare( KWest ) == 0 )
                        {
                        exifLongitude = -exifLongitude;
                        }
                    }
                else
                    {
                    exifError = err;
                    }
                }
            }

        if ( exifError != KErrNone || changed || Abs(exifLongitude - longitude) > KAngleSecond )
            {
            // longitude ref (E/W)
            if ( longitude < 0 )
                {
                exifWriter->SetTagL( EIfdGps,
                		TExifTagInfo( KIdGpsLongitudeRef, CExifTag::ETagAscii, 2 ), west );
                longitude = -longitude;
                }
            else
                {
                exifWriter->SetTagL( EIfdGps,
                		TExifTagInfo( KIdGpsLongitudeRef, CExifTag::ETagAscii, 2 ), east );
                }
            
            TBuf8<KCoordinateBufferSize> longitudeBuf;
            MdsUtils::ConvertFromDecimalToDegreesL( longitude, longitudeBuf );
            exifWriter->SetTagL( EIfdGps,
            		TExifTagInfo( KIdGpsLongitude, CExifTag::ETagRational, 3 ), longitudeBuf );
            exifChanged = ETrue;
            }
        }
    else if ( exifError == KErrNone )
        {
        exifWriter->DeleteTag( EIfdGps, KIdGpsLongitudeRef );
        exifWriter->DeleteTag( EIfdGps, KIdGpsLongitude  );
        exifChanged = ETrue;
        }

    changed = EFalse;
    
    // altitude
    const CExifTag* exifAltitudeRefTag = NULL;
    TRAP( exifError, exifAltitudeRefTag = exifReader->GetTagL( EIfdGps, KIdGpsAltitudeRef ) );
    if ( altitudeProperty )
        {
        TReal64 altitude = altitudeProperty->Real64ValueL();

        TBuf8<8> altitudeBuf;
        const TInt32 KAltDenominator = 100;
        altitude *= KAltDenominator;

        TUint8 altitudeRef = 0;
        TBuf8<1> altRefBuf;
        
        if ( altitude < 0 )
            {
            altitudeRef = 1;
            altitude = -altitude;
            }        	
        
        altRefBuf.Append( &altitudeRef, 1 );

        TInt32 exifAltitudeValue( 0 );
        TInt32 exifAltDenominator( 0 );
        TReal64 exifAltitude = 0.0f;
        TBuf8<1> exifAltitudeRef;
        if ( exifError == KErrNone )
            {
            TPtrC8 exifAltitudeRefBuf( exifAltitudeRefTag->Data() );
            exifAltitudeRef.Append( exifAltitudeRefBuf.Ptr(), 1 );
            const CExifTag* exifAltitudeTag = NULL;
            TRAPD( err, exifAltitudeTag = exifReader->GetTagL( EIfdGps, KIdGpsAltitude ) );
            if ( err == KErrNone )
                {
                TPtrC8 exifAltitudeTagBuf( exifAltitudeTag->Data() );
                memcpy( &exifAltitudeValue, exifAltitudeTagBuf.Ptr(),
                		sizeof(exifAltitudeValue) );
                memcpy( &exifAltDenominator,
                		exifAltitudeTagBuf.Ptr()+sizeof(exifAltitudeValue),
                		sizeof(exifAltDenominator) );
                }
            else
                {
                exifError = err;
                }
            exifAltitude = (TReal64)exifAltitudeValue;
            }

        if ( exifError != KErrNone || exifAltitudeRef.Compare(altRefBuf) ||
        		Abs(altitude - exifAltitude) > KAngleSecond )
            {
            exifWriter->SetTagL( EIfdGps, TExifTagInfo(
            		KIdGpsAltitudeRef, CExifTag::ETagByte, 1 ), altRefBuf );

            TInt32 tmpAlt = (TInt32) altitude;
            altitudeBuf.Append( (TUint8*) &tmpAlt, 4 );
            altitudeBuf.Append( (TUint8*) &KAltDenominator, 4 );
            exifWriter->SetTagL( EIfdGps, TExifTagInfo(
            		KIdGpsAltitude, CExifTag::ETagRational, 1 ), altitudeBuf );
            exifChanged = ETrue;

            changed = EFalse;            
            }
        // measure mode
        const CExifTag* exifMeasureModeTag = NULL;
        TRAPD( err, exifMeasureModeTag = exifReader->GetTagL( EIfdGps, KIdGpsMeasureMode ) );
        if ( err == KErrNone )
        	{
        	
        	TBuf8<2> exifMeasureMode;
            TPtrC8 exifMeasureModeBuf( exifMeasureModeTag->Data() );
            exifMeasureMode.Append( exifMeasureModeBuf.Ptr(), 2 );
        	
            if (altitude == 0)
            	{
            	if (exifMeasureMode.Compare(KMeasureMode3))
            		{
            		changed = ETrue;
            		}
            	}
            else
            	{
            	if (exifMeasureMode.Compare(KMeasureMode2))
            		{
            		changed = ETrue;
            		}
            	}
        	}
        else
            {
            exifError = err;
            }
        
        if (err != KErrNone || changed)
        	{
        	if (altitude == 0)
            	{
            	exifWriter->SetTagL( EIfdGps, TExifTagInfo(
            		KIdGpsMeasureMode, CExifTag::ETagAscii, 2 ), KMeasureMode2 );
            	}
        	else
        		{
        		exifWriter->SetTagL( EIfdGps, TExifTagInfo(
                		KIdGpsMeasureMode, CExifTag::ETagAscii, 2 ), KMeasureMode3 );
        		}
        	}
        }
    else if ( exifError == KErrNone )
        {
        exifWriter->DeleteTag( EIfdGps, KIdGpsAltitudeRef  );
        exifWriter->DeleteTag( EIfdGps, KIdGpsAltitude  );
        exifWriter->DeleteTag( EIfdGps, KIdGpsMeasureMode  );
        exifChanged = ETrue;
        } 
    
    changed = EFalse;
    
    // quality, DOP value
    const CExifTag* exifQualityTag = NULL;
    TRAPD( err, exifQualityTag = exifReader->GetTagL( EIfdGps, KIdGpsDop ) );
    if (qualityProperty)
    	{
        CMdEReal32Property* qualityReal = static_cast<CMdEReal32Property*>(qualityProperty);
        TReal32 quality = qualityReal->Value();
        const TInt32 KQualityDenominator = 10;
        TBuf8<8> qualityBuf;
        TInt32 exifQualityValue( 0 );
        TInt32 exifQualityDenominator( 0 );  
        TReal32 exifQuality (0.0f);
        quality = quality * KQualityDenominator;
        
	    if ( err == KErrNone )
	        {
                TPtrC8 exifQualityTagBuf( exifQualityTag->Data() );
                memcpy( &exifQualityValue, exifQualityTagBuf.Ptr(),
                		sizeof(exifQualityValue) );
                memcpy( &exifQualityDenominator,
                		exifQualityTagBuf.Ptr()+sizeof(exifQualityValue),
                		sizeof(exifQualityDenominator) );
	        }
        else
            {
            exifError = err;
            }
	    
	    if (exifQualityDenominator > 0)
	    	{
	    	exifQuality = (TReal32)exifQualityValue;
	    	}
	    
	    if (exifError != KErrNone || Abs(quality - exifQuality) > 0.1f)
	    	{
	    	
	    	TInt32 tmpQuality = (TInt32) quality;
	    	qualityBuf.Append( (TUint8*) &tmpQuality, 4 );
	    	qualityBuf.Append( (TUint8*) &KQualityDenominator, 4 );
            exifWriter->SetTagL( EIfdGps, TExifTagInfo(
            		KIdGpsDop, CExifTag::ETagRational, 1 ), qualityBuf );
            exifChanged = ETrue;	    	
	    	}	    
    	}
    
    const CExifTag* exifDatumTag = NULL;
    TRAP( err, exifDatumTag = exifReader->GetTagL( EIfdGps, KIdGpsMapDatum ) );
    TBuf8<7> mapdatum( KMapDatum );

    
    if ( err == KErrNone )
        {        
        if (exifDatumTag->Data().Compare(mapdatum))
    		{
    		changed = ETrue;
    		}        	
        }
    else
        {
        exifError = err;
        }
    
    if (exifError != KErrNone || changed)
    	{        		
		exifWriter->SetTagL( EIfdGps, TExifTagInfo(
			KIdGpsMapDatum, CExifTag::ETagAscii, 7 ), mapdatum );
    	}
    
    // write the EXIF data to the image
    if ( exifChanged )
        {
        aModified = exifWriter->WriteDataL( aImagePtr );
        }
    CleanupStack::PopAndDestroy( exifWriter );
    }

void CHarvesterExifUtil::ReadUserCommentL( CHarvestData& aHd, CExifRead* aReader )
    {
    // Getting UserComment
    if ( aReader->TagExists(KIdUserComment, EIfdExif ) )
        {
        TUint16 KMaxCommentLength = 256;
        
        HBufC8* comment = aReader->GetUserCommentL();
        CleanupStack::PushL( comment );
        if( comment->Length() >= 8 )
            {
            TBuf8<8> commentFormat = comment->Mid( 0,8 );
            
            TUint16 commentLength = comment->Length();
            
            TPtrC8 userPtr = comment->Mid( 8, commentLength > KMaxCommentLength ?
                    KMaxCommentLength - 8 : commentLength - 8 );
            
            if ( commentFormat.Compare(KUnicodeCodeDesignation) == 0 )
                {
                WRITELOG( "CHarvesterExifUtil::ReadUserCommentL() - comment, Unicode encoding" );
                aHd.iComment16 = HBufC::NewL( userPtr.Length() );
                TPtr ptr = aHd.iComment16->Des();
                TPtrC16 ptr16( (TUint16*)(userPtr.Ptr()), (userPtr.Size()/2) );
                ptr.Copy( ptr16 );
                }
            else if ( commentFormat.Compare(KAsciiCodeDesignation) == 0 ||
                      commentFormat.Compare(KUnknownCodeDesignation) == 0 ||
                      commentFormat.Compare(KJisCodeDesignation) == 0 )
                {
                aHd.iComment16 = CnvUtfConverter::ConvertToUnicodeFromUtf8L( userPtr ); 
                }
            else
                {
                WRITELOG( "CHarvesterExifUtil::ReadUserCommentL() - unknown comment encoding" );    
                }
            if( aHd.iComment16 )
                {
                StripNulls( *(aHd.iComment16) );
                }
            }
        CleanupStack::PopAndDestroy( comment );
        comment = NULL;
        }
    }

void CHarvesterExifUtil::ReadXResolutionL( CHarvestData& aHd, CExifRead* aReader )
    {
    //Getting X Resolution
    TUint32 numerator = 0;
    TUint32 denominator = 0;
    const TInt error = aReader->GetXResolution( numerator, denominator );
    if ( error == KErrNone )
        {
        aHd.iStoreXResolution = ETrue;
        aHd.iXResolution = 0.0f;
        if ( denominator > 0 )
            {
            aHd.iXResolution = (TReal32) numerator / (TReal32) denominator;
            }
        }
    }

void CHarvesterExifUtil::ReadYResolutionL( CHarvestData& aHd, CExifRead* aReader )
    {
    //Getting Y Resolution
    TUint32 numerator = 0;
    TUint32 denominator = 0;
    const TInt error = aReader->GetYResolution( numerator, denominator );
    if ( error == KErrNone )
        {
        aHd.iStoreYResolution = ETrue;
        aHd.iYResolution = 0.0f;
        if ( denominator > 0 )
            {
            aHd.iYResolution = (TReal32) numerator / (TReal32) denominator;
            }
        }
    }

void CHarvesterExifUtil::ReadExposureBiasL( CHarvestData& aHd, CExifRead* aReader )
    {
    // Getting exposure bias
    TInt32 num( 0 );
    TInt32 deno( 0 );
    const TInt error = aReader->GetExposureBiasValue( num, deno );
    if ( error == KErrNone )
        {
        aHd.iStoreExposureBias = ETrue;
        aHd.iExposureBias = 0.0f;
        if ( deno != 0 )
            {
            aHd.iExposureBias = (TReal32) num / (TReal32) deno;    
            }
        }
    }

void CHarvesterExifUtil::ReadExposureTimeL( CHarvestData& aHd, CExifRead* aReader )
    {
    // Getting exposure time
    TUint32 numerator = 0;
    TUint32 denominator = 0;
    const TInt error = aReader->GetExposureTime( numerator, denominator );
    if ( error == KErrNone )
        {
        aHd.iStoreExposureTime = ETrue;
        aHd.iExposureTime = 0.0f;
        if ( denominator > 0 )
            {
            aHd.iExposureTime = (TReal32) numerator / (TReal32) denominator;
            }
        }
    }

void CHarvesterExifUtil::ReadFNumberL( CHarvestData& aHd, CExifRead* aReader )
    {
    //Getting FNumber
    if ( aReader->TagExists(KIdFNumber, EIfdExif) )
        {
        const CExifTag* tag = aReader->GetTagL( EIfdExif, KIdFNumber );
        TPtrC8 tagData = tag->Data();
        
        TUint32 numerator = MdsUtils::ToUInt32L( CONST_CAST( TUint8*, tagData.Ptr() ) );
        TUint32 denominator = MdsUtils::ToUInt32L( CONST_CAST( TUint8*, tagData.Ptr() + 4 ) );
        if ( denominator == 0 )
            {
            denominator = 1;
            }
        
        aHd.iFNumber = (TReal32) numerator / (TReal32) denominator;
        aHd.iStoreFNumber = ETrue;
        WRITELOG1( "CHarvesterExifUtil::ReadFNumberL() - fnumber %f", aHd.iFNumber );
        }
    }

void CHarvesterExifUtil::ReadShutterSpeedL( CHarvestData& aHd, CExifRead* aReader )
    {
    //Getting shutter speed value
    TInt32 num( 0 );
    TInt32 deno( 0 );
    const TInt error = aReader->GetShutterSpeedValue( num, deno );
    if ( error == KErrNone )
        {
        aHd.iStoreShutterSpeed = ETrue;
        aHd.iShutterSpeed = 0.0f;
        if ( deno != 0 )
            {
            aHd.iShutterSpeed = (TReal32) num / (TReal32) deno;
            }
        
        WRITELOG1( "CHarvesterExifUtil::ReadExifDataL() - shutter speed %f", aHd.iShutterSpeed );
        }
    }

void CHarvesterExifUtil::ReadApertureValueL( CHarvestData& aHd, CExifRead* aReader )
    {
    //Getting aHd.iAperture
    TUint32 numerator = 0;
    TUint32 denominator = 0;
    const TInt error = aReader->GetApertureValue( numerator, denominator );
    if ( error == KErrNone )
        {
        aHd.iStoreAperture = ETrue;
        aHd.iAperture = 0.0f;
        if ( denominator > 0 )
            {
            aHd.iAperture = (TReal32) numerator / (TReal32) denominator;
            }
      
        WRITELOG1( "CHarvesterExifUtil::ReadExifDataL() - aHd.iAperture %f", aHd.iAperture );
        }
    }

void CHarvesterExifUtil::ReadFocalLengthL( CHarvestData& aHd, CExifRead* aReader )
    {
    //Getting focal length
    TUint32 numerator = 0;
    TUint32 denominator = 0;
    if ( aReader->TagExists(KIdFocalLength, EIfdExif) )
        {                
        const CExifTag* tag = aReader->GetTagL( EIfdExif, KIdFocalLength );
        TPtrC8 tagData = tag->Data();
        
        numerator = MdsUtils::ToUInt32L( CONST_CAST( TUint8*, tagData.Ptr() ) );
        denominator = MdsUtils::ToUInt32L( CONST_CAST( TUint8*, tagData.Ptr() + 4 ) );
        
        if ( denominator == 0 )
            {
            denominator = 1;
            }
        
        aHd.iFocalLength = (TReal32) numerator / (TReal32) denominator;
        aHd.iStoreFocalLength = ETrue;
        
        WRITELOG1( "CHarvesterExifUtil::ReadExifDataL() - focal length %f", aHd.iFocalLength );
        }
    }

void CHarvesterExifUtil::ReadFocalLength35mmL( CHarvestData& aHd, CExifRead* aReader )
    {
    // Getting focal length in 35 mm
    if ( aReader->TagExists(KIdFocalLengthIn35mm, EIfdExif) )
        {
        const CExifTag* tag = aReader->GetTagL( EIfdExif, KIdFocalLengthIn35mm );
        TPtrC8 tagData = tag->Data();
        
        aHd.iFocalLengthIn35mm = MdsUtils::ToUInt16L( CONST_CAST( TUint8*, tagData.Ptr() ) );
        
        aHd.iStoreFocalLengthIn35 = ETrue;
        
        WRITELOG1( "CHarvesterExifUtil::ReadExifDataL() - focal length in 35 mm: %f", aHd.iFocalLengthIn35mm );
        }
    }

void CHarvesterExifUtil::ReadFocalXPlaneResolutionL( CHarvestData& aHd, CExifRead* aReader )
    {
    // Getting focal plane X resolution
    if ( aReader->TagExists(KIdFocalPlaneXResolution, EIfdExif) )
        {
        const CExifTag* tag = aReader->GetTagL(
                EIfdExif, KIdFocalPlaneXResolution );
        TPtrC8 tagData = tag->Data();
        
        TUint32 numerator = MdsUtils::ToUInt32L( CONST_CAST( TUint8*, tagData.Ptr() ) );
        TUint32 denominator = MdsUtils::ToUInt32L( CONST_CAST( TUint8*, tagData.Ptr() + 4 ) );
        if ( denominator == 0 )
            {
            denominator = 1;
            }
        
        aHd.iFocalPlaneXResolution = (TReal32) numerator / (TReal32) denominator;
        aHd.iStoreFocalPlaneXResolution = ETrue;
        }
    }

void CHarvesterExifUtil::ReadFocalYPlaneResolutionL( CHarvestData& aHd, CExifRead* aReader )
    {
    // Getting focal plane Y resolution
    if ( aReader->TagExists(KIdFocalPlaneYResolution, EIfdExif) )
        {
        const CExifTag* tag = aReader->GetTagL(
                EIfdExif, KIdFocalPlaneYResolution );
        TPtrC8 tagData = tag->Data();
        
        TUint32 numerator = MdsUtils::ToUInt32L( CONST_CAST( TUint8*, tagData.Ptr() ) );
        TUint32 denominator = MdsUtils::ToUInt32L( CONST_CAST( TUint8*, tagData.Ptr() + 4 ) );
        if ( denominator == 0 )
            {
            denominator = 1;
            }
        
        aHd.iFocalPlaneYResolution = (TReal32) numerator / (TReal32) denominator;
        aHd.iStoreFocalPlaneYResolution = ETrue;
        }
    }

void CHarvesterExifUtil::ReadGPSLatitudeL( CHarvestData& aHd, 
                                                                       CExifRead* aReader, 
                                                                       TBool& aLatitude )
    {
    const TInt KCoordinateBufferSize = 24;
    
    WRITELOG( "CHarvesterExifUtil::ReadGPSLatitudeL() - trying to read GPS Latitude" );
    
    // Getting GPS latitude   
    if ( aReader->TagExists(KIdGpsLatitudeRef, EIfdGps) &&
            aReader->TagExists(KIdGpsLatitude, EIfdGps) )
        {
        WRITELOG( "CHarvesterExifUtil::ReadGPSLatitudeL() - GPS Latitude found" );
        
        TReal64 latitude = 0.0;
        const CExifTag* refTag = aReader->GetTagL(
                EIfdGps, KIdGpsLatitudeRef );
        TBuf8<3> latitudeRef = refTag->Data();
        const CExifTag* latitudeTag = aReader->GetTagL(
                EIfdGps, KIdGpsLatitude );
        TBuf8<KCoordinateBufferSize> latitudeBuf = latitudeTag->Data();
        MdsUtils::ConvertFromDegreesToDecimalL( latitudeBuf, latitude );
        if ( latitudeRef == KSouth )
            {
            latitude = -latitude;
            }
        aHd.iGpsLatitude = latitude;
        aLatitude = ETrue;
        }
    
    WRITELOG( "CHarvesterExifUtil::ReadGPSLatitudeL() - trying to read GPS Latitude - end" );
    }

void CHarvesterExifUtil::ReadGPSLongitudeL( CHarvestData& aHd, 
                                                                          CExifRead* aReader, 
                                                                          TBool& aLatitude )
    {
    const TInt KCoordinateBufferSize = 24;
    
    WRITELOG( "CHarvesterExifUtil::ReadGPSLongitudeL() - trying to read GPS Longitude" );
    
    // Getting GPS longitude
    if ( aReader->TagExists(KIdGpsLongitudeRef, EIfdGps) &&
            aReader->TagExists(KIdGpsLongitude, EIfdGps) )
        {
        WRITELOG( "CHarvesterExifUtil::ReadGPSLatitudeL() - GPS Longitude found" );
    
        TReal64 longitude = 0.0;
        const CExifTag* refTag = aReader->GetTagL(
                EIfdGps, KIdGpsLongitudeRef );
        TBuf8<3> longitudeRef = refTag->Data();
        const CExifTag* longitudeTag = aReader->GetTagL(
                EIfdGps, KIdGpsLongitude );
        TBuf8<KCoordinateBufferSize> longitudeBuf = longitudeTag->Data();
        MdsUtils::ConvertFromDegreesToDecimalL( longitudeBuf, longitude );
        if ( longitudeRef == KWest )
            {
            longitude = -longitude;
            }
        aHd.iGpsLongitude = longitude;
        if ( aLatitude )
            {
            aHd.iStoreGpsLatitudeAndLongitude = ETrue;
            }
        }
    
    WRITELOG( "CHarvesterExifUtil::ReadGPSLongitudeL() - trying to read GPS Longitude - end" );
    }

void CHarvesterExifUtil::ReadGPSAltitudeL( CHarvestData& aHd, CExifRead* aReader )
    {
    const TInt KAltitudeBufferSize = 8;
    
    WRITELOG( "CHarvesterExifUtil::ReadGPSLongitudeL() - trying to read GPS Altitude" );
    
    // Getting GPS altitude
    if ( aReader->TagExists(KIdGpsAltitudeRef, EIfdGps) &&
            aReader->TagExists(KIdGpsAltitude, EIfdGps) )
        {
        WRITELOG( "CHarvesterExifUtil::ReadGPSLatitudeL() - GPS Altitude found" );
    
        const CExifTag* refTag = aReader->GetTagL(
                EIfdGps, KIdGpsAltitudeRef );
        TBuf8<2> altitudeRef = refTag->Data();
        const CExifTag* altitudeTag = aReader->GetTagL(
                EIfdGps, KIdGpsAltitude );
        TBuf8<KAltitudeBufferSize> altitudeBuf = altitudeTag->Data();
        TInt32 altitude = MdsUtils::ToUInt32L(
                CONST_CAST( TUint8*, altitudeBuf.Left(4).Ptr() ) );
        TInt32 denominator =  MdsUtils::ToUInt32L(
                CONST_CAST( TUint8*, altitudeBuf.Right(4).Ptr() ) );
        TInt8 ref = *((TUint8*) altitudeRef.Ptr());

        if ( ref == 1 )
            {
            altitude = -altitude;
            }

        aHd.iGpsAltitude = 0.0f;
        if ( denominator != 0 )
            {
            aHd.iGpsAltitude = (TReal64)altitude / (TReal64)denominator;
            }

        aHd.iStoreGpsAltitude = ETrue;
        }
    
    WRITELOG( "CHarvesterExifUtil::ReadGPSLongitudeL() - trying to read GPS Altitude - end" );
    }

// End of file