mmplugins/imagingplugins/codecs/JPEGCodec/Exif/exifreaderwriter.cpp
changeset 0 40261b775718
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mmplugins/imagingplugins/codecs/JPEGCodec/Exif/exifreaderwriter.cpp	Tue Feb 02 01:56:55 2010 +0200
@@ -0,0 +1,1357 @@
+// Copyright (c) 2004-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:
+//
+
+#include <f32file.h>
+#include "exifreaderwriter.h"
+
+#include "ImageUtils.h"
+#include "ExifTagHelper.h"
+#include "ExifGeneralConsts.h"
+#include "ifdgeneral.h"
+#include "ExifTagDescriptions.h"
+
+// used for ordering exif tags when writing them
+const TInt KNumIfds = 5;
+const TInt KIfdOrder[KNumIfds] = 
+	{
+	EZeroth, 
+	EExifSub,
+	EInterop,
+	EGpsSub,
+	EFirst
+	};
+	
+HBufC8* CExifReaderWriter::CreateExifHeaderBaseL()
+	{
+	//write the GPS IFD tag in IFD 0 - need to write it twice to ensure correct offset
+	//the second write we have the correct offset
+	for (TInt i = 0; i < iIfds.Count(); i++)
+		{
+		if ((iIfds)[i]->Ifd() == EGpsSub)
+			{
+			if ((iIfds)[i]->Size())
+				{
+				TUint gpsIFDoffset = KIfdOffset;
+				TInt result = SetIntegerParam(KTag8825[ETagValue], EZeroth, gpsIFDoffset);
+				if (result != KErrNone)
+					{
+					User::Leave(result);
+					}
+				//loop through IFDs
+				for (TUint j = 0; j < iIfds.Count(); j++)
+	 				{
+	 				//calculate the offset to the GPS IFD 
+	 				TInt ifd = (iIfds)[j]->Ifd();
+	 				if (ifd <= EInterop)
+	 					{
+		 				gpsIFDoffset += (iIfds)[j]->Size() + KSizeOfEntryCount;
+	 					gpsIFDoffset += KIfdOffsetByteCount; // for size of pointer to next Ifd.
+	 					}
+	 				}
+	 			//write the offset to IFD0 8825 GPS tag
+				result = SetIntegerParam(KTag8825[ETagValue], EZeroth, gpsIFDoffset);
+				if (result != KErrNone)
+					{
+					User::Leave(result);
+					}
+				}
+			}
+		}
+		
+	// Need to obtain the size of each IFD object in order to determine initial
+	// alloc size of the total Exif data block.
+	TUint allocSize = 0;
+	
+	// Account for the Exif file header data.
+	allocSize = allocSize + KExifFileHeaderLength;
+	
+	
+	// Account for the thumbnail
+	if(iJpegThumbnailData)
+		{
+		allocSize += iJpegThumbnailData->Length();
+		}
+	
+	// Account for each Ifd.
+	TInt i;
+	for (i = 0; i < iIfds.Count(); i++)
+		{
+		TUint size = iIfds[i]->Size();
+		if (size > 0)
+			{
+			allocSize += KSizeOfEntryCount; 	// Need to allocate room for the Ifd entry count also.
+			allocSize += size;
+			allocSize += KIfdOffsetByteCount;	// Account for the offset to the next Ifd.
+			}
+		}
+
+	
+	// Maximum legal size of APP1 header is 64K.
+	if (allocSize > KMaxApp1Size)
+		{
+		User::Leave(KErrTooBig);
+		}
+		
+	// Allocate required buffer.
+	HBufC8* buffer = HBufC8::NewL(allocSize);
+	CleanupStack::PushL(buffer);
+	iData = const_cast<TUint8*>(buffer->Des().Ptr());
+	TUint8* tmpPtr = iData;
+	
+	WriteExifFileHeaderBaseL(allocSize - KBlockEndMarkerByteCount, tmpPtr);
+		
+	// Write each Ifd in turn.
+	TUint currentOffset = KIfdOffset;
+	for (TInt i = 0; i < KNumIfds; i++)
+		{
+		CIfdGeneral* ifd = GetIfd(KIfdOrder[i]);
+		if (ifd != NULL) 
+			{
+			TUint16 entryCount = ifd->EntryCount();
+			if (entryCount) 
+				{
+				TUint8* tempIfdBlock = ifd->CreateIfdBlockL(currentOffset);
+				
+				// Write Ifd entry count.		
+				tmpPtr = Mem::Copy(tmpPtr, &entryCount, KSizeOfEntryCount);
+				
+				// Write the Ifd to the Exif data block.
+				TInt length = ifd->Size();
+				length += KIfdOffsetByteCount; // for size of pointer to next Ifd.
+				tmpPtr = Mem::Copy(tmpPtr, tempIfdBlock, length);
+						
+				// Free data block created for this Ifd.
+				User::Free(tempIfdBlock);
+
+				currentOffset += length + KSizeOfEntryCount;
+				}		
+			}
+		} // end for
+	
+	SetFirstIfdOffsetBase();
+	
+	// finally, embed the thumbnail
+	if (iJpegThumbnailData)
+		{
+		// Now know the size of the other Ifd's, can set the offset to the 1st Ifd.
+		
+		TUint thumbnailLength = iJpegThumbnailData->Length();
+		//return value the copy operation is not used, so discarded
+		Mem::Copy(tmpPtr, iJpegThumbnailData->Ptr(), thumbnailLength);
+	
+		// No need to account for EOI as it is written elsewhere.
+
+		// If the source does not have JPEGInterchangeFormatLength (tag 0x0202) then
+		// ThumbnailLengthOffset will return an error. 0x0202 is mandatory with thumbs
+		// so this should not happen except with corrupt sources.
+		TUint thumbnailLengthOffset = 0;
+		if (GetThumbnailLengthOffsetBase(thumbnailLengthOffset) == KErrNone)
+			{
+			Mem::Copy(iData + thumbnailLengthOffset, &thumbnailLength, sizeof(TUint));
+			}
+		}
+
+	buffer->Des().SetLength(allocSize);
+	CleanupStack::Pop();
+	return buffer;
+	}
+	
+CExifReaderWriter::CExifReaderWriter() 
+	{
+	}
+
+// Set 8-bit data.
+TInt CExifReaderWriter::SetParam8(TUint aTag, TUint aIfd,  HBufC8* aParam)
+	{
+	TInt err = DoSetParam8(aTag, aIfd, aParam);
+	if(err == KErrNone)
+		{
+		iExifDataModified = ETrue;
+		}
+	return err;
+	}
+
+// Set 16-bit data.  Only used for Unicode 'User Comment' tag.
+TInt CExifReaderWriter::SetParam16(TUint aTag, TUint aIfd,  HBufC16* aParam)
+	{
+	TInt err = DoSetParam16(aTag, aIfd, aParam);
+	if(err == KErrNone)
+		{
+		iExifDataModified = ETrue;
+		}
+	return err;
+	}
+
+TInt CExifReaderWriter::SetIntegerParam(TUint aTag, TUint aIfd,  TInt aParam)
+	{
+	TInt err = DoSetIntegerParam(aTag, aIfd, aParam);
+	if(err == KErrNone)
+		{
+		iExifDataModified = ETrue;
+		}
+	return err;
+	}
+
+
+TInt CExifReaderWriter::SetRationalParam(TUint aTag, TUint aIfd, TInt aNumerator, TInt aDenominator)
+	{
+	TInt err = DoSetRationalParam(aTag, aIfd, aNumerator, aDenominator);
+	if( err == KErrNone)
+		{
+		iExifDataModified = ETrue;
+		}
+	return err;
+	}
+	
+TInt CExifReaderWriter::SetShortParam(TUint aTag, TUint aIfd, TUint16 aParam)
+	{
+	TInt err = DoSetShortParam(aTag, aIfd, aParam);
+	if( err == KErrNone )
+		{
+		iExifDataModified = ETrue;
+		}
+	return err;
+	}
+	
+TInt CExifReaderWriter::SetIntegerArrayParam(TUint aTag, TUint aIfd, CArrayFix<TInt>& aParam)
+	{
+	TInt err = DoSetIntegerArrayParam(aTag, aIfd, aParam);
+	if(err == KErrNone)
+		{
+		iExifDataModified = ETrue;
+		}
+	return err;
+	}
+
+TInt CExifReaderWriter::SetRationalArrayParam(TUint aTag, TUint aIfd, CArrayFix<TRational>& aParam)
+	{
+	TInt err = DoSetRationalArrayParam(aTag, aIfd, aParam);
+	if(err == KErrNone)
+		{
+		iExifDataModified = ETrue;
+		}
+	return err;
+	}
+	
+TInt CExifReaderWriter::SetShortArrayParam(TUint aTag, TUint aIfd, CArrayFix<TUint16>& aParam)
+	{
+	TInt err = DoSetShortArrayParam(aTag, aIfd, aParam);
+	if(err == KErrNone)
+		{
+		iExifDataModified = ETrue;
+		}
+	return err;
+	}
+
+	
+// Set 8-bit data.
+TInt CExifReaderWriter::DoSetParam8(TUint aTag, TUint aIfd,  HBufC8* aParam)
+	{
+	if(aParam == NULL)
+		{
+		return KErrArgument;
+		}
+	
+	TInt internalIfdNumber = 0;
+	CIfdGeneral* internalIfd = NULL;
+	TInt err = KErrNone;
+	TRAP(err, FindInternalIfdDataL(aTag, aIfd, internalIfdNumber, internalIfd));
+	if(err!=KErrNone)
+		{
+		return err;
+		}
+		
+	if(internalIfd->EntryExists(aTag))
+		{
+		// Set the existing entry.
+		return internalIfd->SetParam8(aTag, aParam);	
+		}
+	else
+		{
+		// Get the required parameters.
+		TUint format = 0;
+		err = internalIfd->GetFormat(aTag, format);		
+		if (err == KErrNone) 
+			{
+			TRAP(err, internalIfd->AddParam8L(aTag, format, aParam->Length(), aParam));
+			
+			if (internalIfdNumber == EGpsSub)
+				{
+				ProcessGpsIfdAddParamResult(internalIfd, err);
+				}				
+			}
+			
+		return err;
+		}		
+	}
+
+// Set 16-bit data.  Only used for Unicode 'User Comment' tag.
+TInt CExifReaderWriter::DoSetParam16(TUint aTag, TUint aIfd,  HBufC16* aParam)
+	{
+	if(aParam == NULL)
+		{
+		return KErrArgument;
+		}	
+	
+	// The only tags which may contain 16-bit data are 0x9286 (UserComment),
+	// 0x001B (GpsProcessingMethod) or 0x001C (GpsAreaInformation).
+	if(aTag != KTag9286[ETagValue] && aTag != KTagGPS001B[ETagValue] && aTag != KTagGPS001C[ETagValue])
+		{
+		return KErrArgument;
+		}
+		
+	TInt internalIfdNumber = 0;
+	CIfdGeneral* internalIfd = NULL;
+	TInt err = KErrNone;
+	TRAP(err, FindInternalIfdDataL(aTag, aIfd, internalIfdNumber, internalIfd));
+	if(err!=KErrNone)
+		{
+		return err;
+		}
+		
+	if(internalIfd->EntryExists(aTag))
+		{
+		// Set the existing entry.
+		return internalIfd->SetParam16(aTag, aParam);	
+		}
+	else
+		{
+		// Get the required parameters.
+		TUint format = 0;
+		err = internalIfd->GetFormat(aTag, format);
+		if (err == KErrNone)
+			{
+			TRAP(err, internalIfd->AddParam16L(aTag, format, aParam->Length(), aParam));
+
+			if (internalIfdNumber == EGpsSub)
+				{
+				err = ProcessGpsIfdAddParamResult(internalIfd, err);
+				}
+			}
+
+		return err;
+		}
+	}
+	
+// Sets single integer data (including short, long and integer).
+TInt CExifReaderWriter::DoSetIntegerParam(TUint aTag, TUint aIfd,  TInt aParam)
+	{
+	TInt internalIfdNumber = 0;
+	CIfdGeneral* internalIfd = NULL;
+	TInt err = KErrNone;
+	TRAP(err, FindInternalIfdDataL(aTag, aIfd, internalIfdNumber, internalIfd));
+	if(err!=KErrNone)
+		{
+		return err;
+		}
+		
+	if(internalIfd->EntryExists(aTag))
+		{
+		// Set the existing entry.
+		return internalIfd->SetIntegerParam(aTag, aParam);	
+		}
+	else
+		{
+		// Get the required parameters.
+		TUint format = 0;
+		err = internalIfd->GetFormat(aTag, format);
+		if (err == KErrNone)
+			{
+			// Sets a single integer value.
+			TRAP(err, internalIfd->AddIntegerParamL(aTag, format, 1, aParam));
+			
+			if (internalIfdNumber == EGpsSub)
+				{
+				err = ProcessGpsIfdAddParamResult(internalIfd, err);
+				}
+			}
+			
+		return err;
+		}
+	}
+
+// Sets single rational data (i.e. one numerator and one denominator).
+TInt CExifReaderWriter::DoSetRationalParam(TUint aTag, TUint aIfd, TInt aNumerator, TInt aDenominator)
+	{
+	TInt internalIfdNumber = 0;
+	CIfdGeneral* internalIfd = NULL;
+	TInt err = KErrNone;
+	TRAP(err, FindInternalIfdDataL(aTag, aIfd, internalIfdNumber, internalIfd));
+	if(err!=KErrNone)
+		{
+		return err;
+		}
+
+	if(internalIfd->EntryExists(aTag))
+		{
+		// Set the existing entry.
+		return internalIfd->SetRationalParam(aTag, aNumerator, aDenominator);	
+		}
+	else
+		{
+		// Get the required parameters.
+		TUint format = 0;
+		err = internalIfd->GetFormat(aTag, format);
+		if (err == KErrNone)
+			{
+			TRAP(err, internalIfd->AddRationalParamL(aTag, format, 1, aNumerator, aDenominator));
+
+			if (internalIfdNumber == EGpsSub)
+				{
+				err = ProcessGpsIfdAddParamResult(internalIfd, err);
+				}
+			}
+		return err;
+		}
+
+	}
+
+TInt CExifReaderWriter::DoSetShortParam(TUint aTag, TUint aIfd, TUint16 aParam)
+	{
+	TInt internalIfdNumber = 0;
+	CIfdGeneral* internalIfd = NULL;
+	TInt err = KErrNone;
+	TRAP(err, FindInternalIfdDataL(aTag, aIfd, internalIfdNumber, internalIfd));
+	if(err!=KErrNone)
+		{
+		return err;
+		}
+		
+	if(internalIfd->EntryExists(aTag))
+		{
+		// Set the existing entry.
+		return internalIfd->SetShortParam(aTag, aParam);	
+		}
+	else
+		{
+		// Get the required parameters.
+		TUint format = 0;
+		err = internalIfd->GetFormat(aTag, format);
+		if (err == KErrNone)
+			{
+			// Sets a single integer value.
+			TRAP(err, internalIfd->AddShortParamL(aTag, format, 1, aParam));
+			
+			if (internalIfdNumber == EGpsSub)
+				{
+				err = ProcessGpsIfdAddParamResult(internalIfd, err);
+				}
+			}
+		return err;
+		}
+	}
+
+TInt CExifReaderWriter::DoSetIntegerArrayParam(TUint aTag, TUint aIfd, CArrayFix<TInt>& aParam)
+	{	
+	TInt internalIfdNumber = 0;
+	CIfdGeneral* internalIfd = NULL;
+	TInt err = KErrNone;
+	TRAP(err, FindInternalIfdDataL(aTag, aIfd, internalIfdNumber, internalIfd));
+	if(err!=KErrNone)
+		{
+		return err;
+		}
+	
+	if(internalIfd->EntryExists(aTag))
+		{
+		// Set the existing entry.
+		TRAP(err, internalIfd->SetIntegerArrayParamL(aTag, aParam));	
+		return err;
+		}
+	else
+		{
+		// Get the required parameters.
+		TUint format = 0;
+		err = internalIfd->GetFormat(aTag, format);
+		if (err == KErrNone)
+			{
+			// Sets a single integer value.
+			TRAP(err, internalIfd->AddIntegerArrayParamL(aTag, format, aParam));
+			if (internalIfdNumber == EGpsSub)
+				{
+				err = ProcessGpsIfdAddParamResult(internalIfd, err);
+				}
+			}
+		return err;
+		}
+	}
+	
+TInt CExifReaderWriter::DoSetRationalArrayParam(TUint aTag, TUint aIfd, CArrayFix<TRational>& aParam)
+	{
+	TInt internalIfdNumber = 0;
+	CIfdGeneral* internalIfd = NULL;
+	TInt err = KErrNone;
+	TRAP(err, FindInternalIfdDataL(aTag, aIfd, internalIfdNumber, internalIfd));
+	if(err!=KErrNone)
+		{
+		return err;
+		}
+	
+	if(internalIfd->EntryExists(aTag))
+		{
+		// Set the existing entry.
+		TRAP(err, internalIfd->SetRationalArrayParamL(aTag, aParam));	
+		return err;
+		}
+	else
+		{
+		// Get the required parameters.
+		TUint format = 0;
+		err = internalIfd->GetFormat(aTag, format);
+		if (err == KErrNone)
+			{
+			// Sets a single integer value.
+			TRAP(err, internalIfd->AddRationalArrayParamL(aTag, format, aParam));
+			
+			if (internalIfdNumber == EGpsSub)
+				{
+				err = ProcessGpsIfdAddParamResult(internalIfd, err);
+				}
+			}
+			
+		return err;
+		}
+	}
+
+TInt CExifReaderWriter::DoSetShortArrayParam(TUint aTag, TUint aIfd, CArrayFix<TUint16>& aParam)
+	{
+	TInt internalIfdNumber = 0;
+	CIfdGeneral* internalIfd = NULL;
+	TInt err = KErrNone;
+	TRAP(err, FindInternalIfdDataL(aTag, aIfd, internalIfdNumber, internalIfd));
+	if(err!=KErrNone)
+		{
+		return err;
+		}
+			
+	if(internalIfd->EntryExists(aTag))
+		{
+		// Set the existing entry.
+		TRAP(err, internalIfd->SetShortArrayParamL(aTag, aParam));	
+		return err;
+		}
+	else
+		{
+		// Get the required parameters.
+		TUint format = 0;
+		err = internalIfd->GetFormat(aTag, format);
+		if (err == KErrNone)
+			{
+			// Sets a single integer value.
+			TRAP(err, internalIfd->AddShortArrayParamL(aTag, format, aParam));
+			if (internalIfdNumber == EGpsSub)
+				{
+				err = ProcessGpsIfdAddParamResult(internalIfd, err);
+				}
+			}
+		return err;
+		}
+	}
+
+
+CExifReaderWriter::~CExifReaderWriter()
+	{
+	iIfds.ResetAndDestroy();
+	delete iJpegThumbnailData;
+	}
+
+TInt CExifReaderWriter::GetIntegerParam(TUint aTag, TUint aIfd, TInt& aParam) const
+	{
+	TInt err = KErrNotFound;
+	CIfdGeneral* ifd = FindIfd(aTag, aIfd);
+	
+	if (ifd != NULL)
+		{
+		err = ifd->GetIntegerParam(aTag, aParam);	
+		}
+	return err;
+	}
+
+TInt CExifReaderWriter::GetShortParam(TUint aTag, TUint aIfd, TUint16& aParam) const
+	{
+	TInt err = KErrNotFound;
+	CIfdGeneral* ifd = FindIfd(aTag, aIfd);
+	
+	if (ifd != NULL)
+		{
+		err = ifd->GetShortParam(aTag, aParam);	
+		}
+	return err;
+	}
+
+TInt CExifReaderWriter::GetRationalParam(TUint aTag, TUint aIfd, TInt& aNumer, TInt& aDenom) const
+	{
+	TInt err = KErrNotFound;
+	CIfdGeneral* ifd = FindIfd(aTag, aIfd);
+	
+	if (ifd != NULL)
+		{
+		err = ifd->GetRationalParam(aTag, aNumer, aDenom);	
+		}
+	return err;
+	}
+
+TInt CExifReaderWriter::GetParam8(TUint aTag, TUint aIfd, HBufC8*& aParam) const
+	{
+	TInt err = KErrNotFound;
+	CIfdGeneral* ifd = FindIfd(aTag, aIfd);
+	
+	if (ifd != NULL)
+		{
+		err = ifd->GetParam8(aTag, aParam);	
+		}
+	return err;
+	}
+
+TInt CExifReaderWriter::GetParam16(TUint aTag, TUint aIfd, HBufC16*& aParam) const
+	{
+	// The only tags which may contain 16-bit data are 0x9286 (UserComment),
+	// 0x001B (GpsProcessingMethod) or 0x001C (GpsAreaInformation).
+	if(aTag != KTag9286[ETagValue] && aTag != KTagGPS001B[ETagValue] && aTag != KTagGPS001C[ETagValue])
+		{
+		return KErrArgument;
+		}
+		
+	TInt err = KErrNotFound;
+	CIfdGeneral* ifd = FindIfd(aTag, aIfd);
+	
+	if (ifd != NULL)
+		{
+		err = ifd->GetParam16(aTag, aParam);	
+		}
+	return err;
+	}
+
+TInt CExifReaderWriter::GetIntegerArrayParam(TUint aTag, TUint aIfd, CArrayFix<TInt>& aParam) const
+	{
+	TInt err = KErrNotFound;
+	CIfdGeneral* ifd = FindIfd(aTag, aIfd);
+	
+	if (ifd != NULL)
+		{
+		TRAP(err, ifd->GetIntegerArrayParamL(aTag, aParam));	
+		}
+	return err;
+	}
+
+TInt CExifReaderWriter::GetShortArrayParam(TUint aTag, TUint aIfd, CArrayFix<TUint16>& aParam) const
+	{
+	TInt err = KErrNotFound;
+	CIfdGeneral* ifd = FindIfd(aTag, aIfd);
+	
+	if (ifd != NULL)
+		{
+		TRAP(err, ifd->GetShortArrayParamL(aTag, aParam));	
+		}
+	return err;
+	}
+
+TInt CExifReaderWriter::GetRationalArrayParam(TUint aTag, TUint aIfd, CArrayFix<TRational>& aParam) const
+	{
+	TInt err = KErrNotFound;
+	CIfdGeneral* ifd = FindIfd(aTag, aIfd);
+	
+	if (ifd != NULL)
+		{
+		TRAP(err, ifd->GetRationalArrayParamL(aTag, aParam));	
+		}
+	return err;
+	}
+
+void CExifReaderWriter::InitializeIfdsL(TBool aAlwaysCreateIfd1, TBool aCreateGpsIfd)
+	{
+	// All of the IFD's should be present, because each has at least one Mandatory Ifd entry.	
+	CIfd0* ifd0 = CIfd0::NewLC();
+	iIfds.AppendL(ifd0);
+	CleanupStack::Pop(ifd0);
+	
+	CExifIfd* exifIfd = CExifIfd::NewLC();
+	iIfds.AppendL(exifIfd);
+	CleanupStack::Pop(exifIfd);
+	
+	CInteropIfd* interopIfd = CInteropIfd::NewLC();
+	iIfds.AppendL(interopIfd);
+	CleanupStack::Pop(interopIfd);
+	 
+	if(iEncodeThumbnail || aAlwaysCreateIfd1)
+		{
+		CIfd1* ifd1 = CIfd1::NewLC();
+		iIfds.AppendL(ifd1);
+		CleanupStack::Pop(ifd1);
+		}	
+		
+	if (aCreateGpsIfd) 
+		{
+		CGpsIfd* gpsIfd = CGpsIfd::NewLC();
+		iIfds.AppendL(gpsIfd);
+		CleanupStack::Pop(gpsIfd);
+		}
+	}
+
+
+void CExifReaderWriter::InitializeIfdsL(TUint8* aData, TUint aApp1Size)
+	{
+	iData = aData; 
+	
+	// Note that iApp1Size includes 2bytes for the APP1 length field and 6bytes for the
+	// EXIF header so these both need to be subtracted to determine the end of the EXIF
+	// data.
+	iExifDataLength = aApp1Size - KOffsetFromExifHeaderToByteAlignment - KApp1SizeLength;
+	if(iData)
+		{
+		ReadHeaderL();
+		SetUpIfdsL();
+		}
+	}
+	
+// Returns the internal Ifd (i.e. EIfd values) from the given external Ifd (i.e. 0th or 1st)
+// that contains the provided tag.  This is because the Exif Sub Ifd and the Interoperability
+// Ifd are both considered to be part of the 0th Ifd.
+// Returns KErrArgument if not valid.
+TInt CExifReaderWriter::InternalIfd(const TUint aTag, const TInt aExternalIfd) const
+	{
+	if(aExternalIfd == 0)
+		{
+		// Can be either 0th, Exif Sub or Interoperability Ifd.
+		if(TExifTagHelper::IsCorrectParentIfd(aTag, EZeroth))
+			{
+			return EZeroth;			
+			}
+		if(TExifTagHelper::IsCorrectParentIfd(aTag, EExifSub))
+			{
+			return EExifSub;
+			}
+		if(TExifTagHelper::IsCorrectParentIfd(aTag, EInterop))
+			{
+			return EInterop;
+			}
+		if(TExifTagHelper::IsCorrectParentIfd(aTag, EGpsSub))
+			{
+			return EGpsSub;
+			}		
+		}
+	else if(aExternalIfd == 1)
+		{
+		if(TExifTagHelper::IsCorrectParentIfd(aTag, EFirst))
+			{
+			return EFirst;
+			}
+		}
+
+	//Check if upper 16 bits are used to specifiy a sub-IFD.
+ 	//Only IFD 0 sub IFDs can currently be checked for.
+ 	else if (TExifIfdHelper::IfdContainsIfd0SubIfd(aExternalIfd, EGpsSub))
+ 		{
+ 		return EGpsSub;
+ 		}
+	return KErrNotFound;
+	}
+	
+TInt CExifReaderWriter::CheckTagL(const TUint aTag, const TUint aIfd) const
+	{
+	TInt realIfd = InternalIfd(aTag, aIfd);
+	
+	if (!TExifIfdHelper::IsValidInternalIfd(realIfd))
+		{
+		User::Leave(KErrNotFound);
+		}
+	return realIfd;
+	}
+
+
+// Updates aIfdIndex with the index of the given IFD if found.
+TBool CExifReaderWriter::FindIfdIndex(TInt aRealIfd, TInt& aIfdIndex) const
+	{
+	for(TInt i = 0; i < iIfds.Count(); ++i)
+		{
+		if(iIfds[i]->Ifd() == aRealIfd)
+			{
+			aIfdIndex = i;
+			return ETrue;
+			}
+		}
+	return EFalse;
+	}
+
+// Returns the CIfdGeneral* for this tag/ifd pair if it exists, else NULL
+CIfdGeneral* CExifReaderWriter::FindIfd(const TUint aTag, const TUint aExternalIfd) const
+	{
+	TInt realIfd=0;
+	TInt err = KErrNone;
+	TRAP(err, realIfd=CheckTagL(aTag, aExternalIfd));
+	if(err==KErrNone)
+		{		
+		TInt ifdIndex=0;
+		if(FindIfdIndex(realIfd, ifdIndex))
+			{
+			return iIfds[ifdIndex];
+			}
+		}
+	return NULL;
+	}
+	
+// This is the preamble for most Set functions.  Its purpose is to fetch the Internal
+// Ifd information for the given aTag/aExternalIfdNumber pair.  The Internal Ifd 
+// information fetched consists of a pointer to the CIfd object and the Internal Ifd 
+// Number.
+//
+// Note: If aExternalIfdNumber corresponds to the GPS Ifd, then it is possible that the 
+// Ifd object for it does not exist yet.  This is because the GPS Ifd has no mandatory
+// tags so it need not be present in metadata.  In this case, this method will create
+// the Ifd object for GPS & add it to the iIfds array before returning its information
+// to the caller.
+//
+// If it can not return an internal representation for the given aTag/aExternalIfdNumber 
+// pair, then this method leaves with KErrNotSupported.
+void CExifReaderWriter::FindInternalIfdDataL(const TUint aTag, const TUint aExternalIfdNumber, TInt& aInternalIfdNumber, CIfdGeneral*& aInternalIfd)
+	{
+	aInternalIfdNumber = CheckTagL(aTag, aExternalIfdNumber);
+
+	//All the CExifReaderWriter::Set() methods used to use the realIfd as the array index.
+	//However, since the GPS IFD is optional this is invalid & now we check to get 
+	//the correct array index.	
+	TInt ifdIndex = 0;
+	if (!FindIfdIndex(aInternalIfdNumber, ifdIndex))
+		{
+		//ifd does not yet exist internally
+		if (aInternalIfdNumber == EGpsSub)
+			{
+			//Currently this can only be GPS IFD since the other IFDs have madatory entries.
+			//We need to create an empty GPS ifd. First create GPS IFD pointer in 0 IFD
+			//We don't care about offset value at this point - this gets written in TExifAccessor::CreateExifHeaderL
+			CGpsIfd* gpsIfd = CGpsIfd::NewLC(0, NULL, ETrue);
+			iIfds.AppendL(gpsIfd);
+			CleanupStack::Pop(gpsIfd);
+			aInternalIfd = gpsIfd;
+			}
+		else
+			{
+			//ifd is real but there are no entries
+			User::Leave(KErrNotFound);
+			}
+		}
+	else
+		{
+		aInternalIfd = iIfds[ifdIndex];		
+		}	
+	}
+
+// Helper Function
+/*static*/
+TUint CExifReaderWriter::ReadUint32(const TBool aIntelByteAlign, const TUint8* aPtr)
+	{
+	if(aIntelByteAlign)
+		{
+		return PtrReadUtil::ReadUint32(aPtr);
+		}
+	else // Motorola
+		{
+		return PtrReadUtil::ReadBigEndianUint32(aPtr);
+		}
+	}
+
+// Helper Function	
+/*static*/
+TUint16 CExifReaderWriter::ReadUint16(const TBool aIntelByteAlign, const TUint8* aPtr)
+	{
+	if(aIntelByteAlign)
+		{
+		return PtrReadUtil::ReadUint16(aPtr);
+		}
+	else // Motorola
+		{
+		return PtrReadUtil::ReadBigEndianUint16(aPtr);
+		}
+	}
+
+
+void CExifReaderWriter::ReadHeaderL()
+	{
+	// Check integrity of header.
+	TPtrC8 exifHeaderPtr(iData, KExifHeaderLength);
+	if(KExifHeader().Compare(exifHeaderPtr))
+		{
+		User::Leave(KErrCorrupt);
+		}
+	
+	TUint16 byteAlignment = 0;	
+	Mem::Copy(&byteAlignment, iData+KOffsetFromExifHeaderToByteAlignment, sizeof(byteAlignment));
+	if(byteAlignment == KIntelByteAlignment)
+		{
+		iIntel = ETrue;		
+		}
+	else if(byteAlignment == KMotorolaByteAlignment)
+		{
+		iIntel = EFalse;
+		}
+	else
+		{
+		User::Leave(KErrCorrupt);
+		}
+			
+	iBase = iData+KOffsetFromExifHeaderToByteAlignment;
+	
+	
+	TUint16 readFortyTwo = ReadUint16(iIntel, iData + KOffsetFromExifHeaderToByteAlignment + sizeof(byteAlignment));
+	if (readFortyTwo != KFortyTwo)
+		{
+		readFortyTwo = ReadUint16(!iIntel, iData + KOffsetFromExifHeaderToByteAlignment + sizeof(byteAlignment));
+		if (readFortyTwo != KFortyTwo)
+			{
+			User::Leave(KErrArgument);
+			}
+		}
+		
+	iOffsetToZerothIfd = ReadUint32(iIntel, iData + KOffsetToZerothOffset);
+	if ( CheckExifOffset(iOffsetToZerothIfd) != KErrNone )
+		{
+		iOffsetToZerothIfd = ReadUint32(!iIntel, iData + KOffsetToZerothOffset);
+		if ( CheckExifOffset(iOffsetToZerothIfd) != KErrNone )
+			{
+			// If we've got this far then it probably really is an exif file
+			// but the offset is corrupt
+			iOffsetToZerothIfd = KIfdOffset; //so we'll try a default value
+			}
+		}
+	}
+
+/**
+	Attempts to create a sub-IFD whose offset is given by an entry in another IFD and append
+	it to iIfds.
+	However, if the given tag is not found, the offset is wrong or the IFD data is corrupt
+	then nothing will be appended to iIfds and NULL shall be returned.
+	
+	@param	aIfd	The IFD containing the entry with the offset to the sub-IFD.
+	@param	aTag	The tag of the entry containing the offset.
+
+	@return	Pointer to the sub-IFD that was created and appended to iIFds
+			or NULL if it could not be created. (This does not transfer ownership
+			of the IFD since it is owned by iIfds!)
+			
+	@leave	KErrNoMemory	If the sub-IFD could not be created due to OOM. (This
+							is the only type of leave this function can make!)
+	
+*/
+CIfdGeneral* CExifReaderWriter::CreateAndAppendSubIfdL(CIfdGeneral& aIfd, TUint aTag)
+	{
+	TInt offsetValue = 0;
+	CIfdGeneral* subIfd = NULL;
+	
+	TInt err = aIfd.GetIntegerParam( aTag, offsetValue );
+	if(err == KErrNone)
+		{		
+		// offset tag found	
+		if((err = CheckExifOffset( offsetValue )) != KErrNone)
+			{
+			// offset wrong
+			aIfd.RemoveEntryL( aTag );
+			}
+		else
+			{
+			// offset OK - try to create IFD
+			if( aTag == KTag8769[ETagValue] )
+				{
+				// EXIF sub-IFD
+				TRAP(err, subIfd = CExifIfd::NewL(offsetValue, iBase, iIntel, iExifDataLength) );
+				}
+			else if( aTag == KTagA005[ETagValue] )
+				{
+				// Interoperability sub-IFD
+				TRAP(err, subIfd = CInteropIfd::NewL(offsetValue, iBase, iIntel, iExifDataLength) );
+				}
+			else if( aTag == KTag8825[ETagValue] )
+				{
+				// GPS sub-IFD
+				TRAP(err, subIfd = CGpsIfd::NewL(offsetValue, iBase, iIntel, iExifDataLength) );
+				}
+			else
+				{
+				return NULL;
+				}
+				
+			if( err != KErrNone )
+				{
+				// could not create IFD
+				if( err == KErrNoMemory )
+					{
+					User::Leave( err );
+					}
+				else
+					{
+					aIfd.RemoveEntryL( aTag );
+					}
+				}
+			else
+				{
+				// IFD created successfully
+				CleanupStack::PushL(subIfd);
+				iIfds.AppendL(subIfd);
+				CleanupStack::Pop(subIfd);
+				}
+			}
+		}
+		return subIfd;
+	}
+
+/**
+	Extracts IFDs and their entries from the EXIF data.
+	If entries in an IFD are corrupt they will be ignored. If many corrupt
+	entries occur in sequence the(sub-)IFD that contains them is considered
+	corrupt and ignored. (See CIfdGeneral::AddAllIfdEntriesL() for more details)
+	
+	If IFD0 is corrupt this function will leave with KErrCorrupt, if any of the other
+	(sub-)IFDs are corrupt they are ignored and the corresponding entry that points
+	to them is removed. (e.g.: If the EXIF IFD is found to be corrupt then the EXIF IFD
+	offset tag (0x8769) in IFD0 will be removed)
+
+	@see 	CIfdGeneral::AddAllIfdEntriesL() 
+	
+	@leave	KErrNoMemory	If there is insufficient memory to process the data
+	@leave	KErrCorrupt		If IFD0 is too corrupted for anything to be reliably
+							extracted from it.
+*/
+void CExifReaderWriter::SetUpIfdsL()
+	{
+	// IFD0
+	CIfd0* ifd0 = CIfd0::NewLC(iOffsetToZerothIfd, iBase, iIntel, iExifDataLength);
+	iIfds.AppendL(ifd0);
+	CleanupStack::Pop(ifd0);
+	
+	// EXIF sub-IFD
+	CIfdGeneral* exifIfd = CreateAndAppendSubIfdL( *ifd0, KTag8769[ETagValue]);
+	if( exifIfd )
+		{
+		// Interoperability sub-IFD
+		CreateAndAppendSubIfdL( *exifIfd, KTagA005[ETagValue]);
+		}
+		
+	// GPS sub-IFD
+	CreateAndAppendSubIfdL( *ifd0, KTag8825[ETagValue]);
+
+	// IFD1	
+	TRAPD(err, SetIfd1L());
+	if( err == KErrNoMemory )
+		{
+		User::Leave(err);
+		}
+	// else ignore
+	}
+
+/**
+	Check that an offset relative to the EXIF data is valid.
+	Note that offsets in the EXIF data are relative to iBase (the
+	start of the EXIF data contained within the APP1 block) and not 
+	iData (the start of the APP1 block).
+	
+	@param	aOffset	An offset pointing to an IFD or value within
+					the EXIF data.
+					
+	@return	KErrNone	If the offset is between KIfdOffset (the 8 bytes of
+						the TIFF header) and the end of the EXIF data (the
+						end of the APP1 block minus the APP1 headers that
+						precede the EXIF data)
+			KErrCorrupt	If the offset is outside that range.
+	
+*/
+TInt CExifReaderWriter::CheckExifOffset(const TUint aOffset)
+	{
+	TInt err = KErrNone;
+	// ensure that aOffset is within a valid section of the EXIF data
+	if ((aOffset < KIfdOffset) || (aOffset >= iExifDataLength))
+		{
+		err = KErrCorrupt;
+		}
+	return err;
+	}
+	
+// This method is to be called after at attempt was made to add a param to a GPS Ifd.
+// It must only be called with the GPS Ifd.
+// 
+// Its purpose is to process the result (aAddParamResult) of a CIfd::AddParamXXX() 
+// operation on a GPS Ifd.
+//
+// It will add the GPS Ifd pointer to Ifd 0 if a param was sucessfully added to a 
+// brand new GPS Ifd. (i.e. if aErr==KErrNone && aGpsIfd->EntryCount() == 1).
+//
+// If an attempt to add a param to a brand new GPS Ifd failed, or if the GPS Ifd pointer
+// could not be added to Ifd 0, then the GPS Ifd is deleted and is returned.
+TInt CExifReaderWriter::ProcessGpsIfdAddParamResult(CIfdGeneral*& aGpsIfd, TInt aAddParamResult)
+	{
+	ASSERT(aGpsIfd != NULL);
+	ASSERT(aGpsIfd->Ifd() == EGpsSub);
+
+	TInt result = KErrNone;
+	if ((aAddParamResult==KErrNone) && (aGpsIfd->EntryCount() == 1))
+		{
+		// The very first GPS tag has just been added to this Ifd, so add GPS Ifd pointer 
+		// to Ifd 0.  Don't care about offset value at this point - this gets written in 
+		// TExifAccessor::CreateExifHeaderL()
+		result = SetIntegerParam(KTag8825[ETagValue],EZeroth, 0);
+		}
+		
+	if ((result != KErrNone) || (aGpsIfd->EntryCount() == 0))
+		{
+		// Either it was not possible to add a GPS Ifd Pointer to Ifd 0, or
+		// The GPS Ifd has no tags.  In either case, it should be removed.
+		TInt gpsIndex = 0;
+		if (FindIfdIndex(EGpsSub, gpsIndex))
+			{
+			delete iIfds[gpsIndex];
+			iIfds.Remove(gpsIndex);
+			}
+		if (result == KErrNone)
+			{
+			//Can only be here if aAddParamResult != KErrNone.
+			result = aAddParamResult;
+			}
+		}
+	return result;
+	}
+	
+	
+void CExifReaderWriter::RemoveThumbnailData()
+	{	
+	for(TInt i=0; i<iIfds.Count(); i++)
+		{
+		if((iIfds)[i]->Ifd()==EFirst)
+			{
+			delete iIfds[i];
+			iIfds.Remove(i);
+			return;
+			}
+		}
+	}
+	
+	
+CIfdGeneral* CExifReaderWriter::GetIfd(TInt aIfd)
+	{
+	TInt index;
+	if (FindIfdIndex(aIfd, index))
+		return iIfds[index];	
+	else
+		return NULL;
+	}
+
+
+
+// Will only set the thumbnail if it is JPEG encoded.
+void CExifReaderWriter::SetThumbnailDataBaseL(CIfdGeneral* ifd1, TBool aKeepIfd1)
+	{
+	ASSERT(ifd1);
+	
+	delete iJpegThumbnailData;
+	iJpegThumbnailData = NULL;
+	
+	// Check for tag 0x0103 (Compression). It must indicate JPEG.
+	TUint16 jpegCompressionValue = 0;	
+	TInt err = ifd1->GetShortParam(KThumbTag0103[ETagValue], jpegCompressionValue);
+	if(err == KErrNone && jpegCompressionValue == KJPEGEncodedThumbnail)
+		{
+		// This is a JPEG thumbnail. Check for tag 0x0201 (offset to the JPEG thumbnail SOI).
+ 		TInt offset = 0;	
+ 
+ 		err = ifd1->GetIntegerParam(KThumbTag0201[ETagValue], offset);
+		if(err == KErrNone)
+ 			{
+ 			// Check for tag 0x0202 (size of the JPEG thumbnail).
+ 			TInt thumbSize = 0;
+ 			
+ 			err = ifd1->GetIntegerParam(KThumbTag0202[ETagValue], thumbSize);
+ 			if (err == KErrNone)
+ 				{
+				TInt sizeTIFFHeader = KByteAlignmentByteCount + KFortyTwoByteCount + KIfdOffsetByteCount; 
+			                                                
+				if (thumbSize != (iExifDataLength - offset))
+					{
+					// incorrect offset/thumbSize - try correcting
+					if ((offset >= sizeTIFFHeader)	&& (offset < iExifDataLength))
+						{
+						// offset looks good so adjust the size
+						thumbSize = iExifDataLength - offset;
+						}
+					else if ((thumbSize >= 0) && (thumbSize < (iExifDataLength - sizeTIFFHeader)))
+						{
+						// size looks good so adjust the offset
+						offset = iExifDataLength - thumbSize;
+						}
+					else
+						{
+						// can't be corrected - ignore thumbnail
+						thumbSize = 0;
+						}				
+					}
+ 				// If the size is valid, read thumbSize bytes at offset from iBase.
+				if (thumbSize > 0)
+ 					{			
+ 					iJpegThumbnailData = HBufC8::NewL(thumbSize);
+ 					iJpegThumbnailData->Des().Copy(&iBase[offset], thumbSize);
+					return;	
+					}			
+				} // Check for 0x0202
+			} // Check for 0x0201
+		} // JPEG thumbnail
+
+	// Either:
+	// -	The source IFD1 did not contain tag 0x0103 (Compression);
+	// -	We have an uncompressed (bitmap) thumbnail (jpegCompressionValue == 1);
+	// -	We have a thumbnail of an unknown/unsupported compression type. 
+	// -	There were unrecoverable errors with tags 0x0201 (offset to thumbnail SOI) and 0x0202 (size of thumbnail).
+	//
+	// Bitmap thumbnails are supported by the Exif spec, but we do not currently have 
+	// the means to support them fully in our software. Likewise we have no way of 
+	// dealing with unknown compression types or images with badly corrupt 0x0201 and/or 0x0202.
+	// The only thing to do is remove the corrupt copy of the source IFD1 from iIfds and
+	// replace it with a default (which will be populated or removed as necessary later on).
+
+	// Remove the old IFD1 that describes the source thumbnail image
+	RemoveThumbnailData();
+
+	
+	if (aKeepIfd1)
+		{
+		// Add a new default IFD1 to describe the thumbnail image that we will generate.
+		ifd1 = CIfd1::NewLC(0, NULL, iIntel);
+		iIfds.AppendL(ifd1);
+		CleanupStack::Pop(ifd1);		
+		}
+	}
+
+
+
+
+// Copies the standard Exif file header stuff to the given buffer.
+void CExifReaderWriter::WriteExifFileHeaderBaseL(const TUint aApp1Size, TUint8*& aPtr)
+	{
+	if(aApp1Size < KExifFileHeaderLength)
+		{
+		User::Leave(KErrNoMemory);
+		}
+	aPtr = Mem::Copy(aPtr, &KApp1Marker, KApp1MarkerByteCount);
+	TUint8 appSizePtr[KApp1SizeLength];
+	PtrWriteUtil::WriteBigEndianInt16(appSizePtr,aApp1Size);
+	aPtr = Mem::Copy(aPtr, &appSizePtr, KApp1SizeLength);
+	aPtr = Mem::Copy(aPtr, &KExifMarker, KExifMarkerByteCount);
+	aPtr = Mem::Copy(aPtr, &KExifMarkerEnd, KExifMarkerEndByteCount);
+	aPtr = Mem::Copy(aPtr, &KIntelByteAlignment, KByteAlignmentByteCount);
+	aPtr = Mem::Copy(aPtr, &KFortyTwo, KFortyTwoByteCount);
+	aPtr = Mem::Copy(aPtr, &KIfdOffset, KIfdOffsetByteCount);	
+	}
+	
+void CExifReaderWriter::SetFirstIfdOffsetBase()
+	{
+	// Only set the first ifd offset if we have thumbnail data, or we will encode it
+	if (iEncodeThumbnail || iJpegThumbnailData) 
+		{
+		// Locate the 1st Ifd offset - immediately after all tag data in the 0th Ifd (and before offset data).
+		TUint offsetToFirstOffsetField = KExifFileHeaderLength;	
+		CIfdGeneral* ifd = GetIfd(EZeroth);
+		ASSERT(ifd);
+		TUint16 size = ifd->EntryCount() * KMinimumIfdEntrySize;
+		offsetToFirstOffsetField += size + KSizeOfEntryCount; // Need to account for the Ifd entry count.
+		
+		TInt offsetValue = KExifFileHeaderLength;
+		// get the sizes of the ifds
+		for(TInt i = 0; i < iIfds.Count(); i++)
+			{
+			CIfdGeneral* iFd = (iIfds)[i];
+			TInt id = iFd->Ifd();
+			if ((id == EZeroth)	||
+				(id == EExifSub) ||
+				(id == EInterop) ||
+				(id == EGpsSub))
+				{
+				if (iFd->Size() != 0) 
+					{
+					offsetValue += KSizeOfEntryCount + iFd->Size() + KIfdOffsetByteCount;	
+					}
+				}
+			}
+
+		// Number of bytes from the start of the Exif header to the 1st Ifd.
+		// Since all offsets are calculated from the byte alignment, deduct this.
+		offsetValue -= KByteCountToByteAlignment;	
+		Mem::Copy(iData + offsetToFirstOffsetField, &offsetValue, sizeof(offsetValue));
+		}
+
+	}
+	
+	
+TInt CExifReaderWriter::GetThumbnailLengthOffsetBase(TUint& aThumbnailLengthOffset)
+	{
+	TUint offsetToLengthField = 0;
+	TUint numFirstIfdEntries = 0;
+
+	offsetToLengthField += KExifFileHeaderLength;
+	
+	for (TInt i = 0; i < iIfds.Count(); i++)
+		{
+		TUint16 size = (iIfds)[i]->Size();
+		if ((size > 0) && ((iIfds)[i]->Ifd() != EFirst))
+			{
+			offsetToLengthField += KSizeOfEntryCount; // Need to account for the Ifd entry count.
+			offsetToLengthField += KIfdOffsetByteCount; // Need to account for the offset to the 1st Ifd.
+			offsetToLengthField += size;
+			}
+		else if ((iIfds)[i]->Ifd() == EFirst)
+			{
+			numFirstIfdEntries = (iIfds)[i]->EntryCount(); 	
+			}
+		}
+		
+	offsetToLengthField += KSizeOfEntryCount; // For first Ifd's entry count.
+	
+	TUint8* tempPtr = iData + offsetToLengthField; // point to first entry in 1st Ifd.
+	
+	// Now step every 12 bytes to find location of the tag.
+	TUint16 jpegLengthTag = KThumbTag0202[ETagValue];
+	for (TUint j = 0; j < numFirstIfdEntries; j++)
+		{
+		TUint16 tag = PtrReadUtil::ReadUint16(tempPtr);
+		if (tag == jpegLengthTag)
+			{						
+			aThumbnailLengthOffset = offsetToLengthField+KValueOffsetFieldPosition;
+			return KErrNone;
+			}
+		offsetToLengthField += KMinimumIfdEntrySize;
+		tempPtr += KMinimumIfdEntrySize;
+		}	
+	return KErrNotFound;
+	}
+	
+HBufC8* CExifReaderWriter::GetJpegThumbnailData()
+	{
+	// don't delete previous parameter, this makes no sense in this case
+	return iJpegThumbnailData;
+	}
+	
+void CExifReaderWriter::SetThumbnailData(HBufC8* aJpegData)
+	{
+	delete iJpegThumbnailData;
+	iJpegThumbnailData= aJpegData;
+	if(iJpegThumbnailData != NULL) //Ensure new thumbnail data is added
+		{
+		iExifDataModified = ETrue;
+		}
+	}
+
+TBool CExifReaderWriter::IsExifDataModified() 
+	{
+	return iExifDataModified;
+	}
+	
+void CExifReaderWriter::ResetExifDataModified() 
+	{
+	iExifDataModified = EFalse;
+	}