imaging/imagingplugins/codecs/JPEGCodec/Exif/ExifEditUtility.cpp
changeset 0 5752a19fdefe
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/imaging/imagingplugins/codecs/JPEGCodec/Exif/ExifEditUtility.cpp	Wed Aug 25 12:29:52 2010 +0300
@@ -0,0 +1,875 @@
+// 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 "exiftransform.h"
+#include "exiftransformdataaccessor.h"
+#include "ExifEditUtility.h"
+#include "ExifThumbnailGenerator.h"
+#include "ExifGeneralConsts.h"
+#include "JpegConsts.h"
+#include "ImageUtils.h"
+
+#include <f32file.h>
+
+const TUint8 KJpegMarkerByte = 0xff;
+const TUint8 KDhtMarkerByte = 0xc4;
+const TUint8 KDqtMarkerByte = 0xdb;
+const TUint8 KSosMarkerByte = 0xda;
+
+
+const TUint KTableTypeSize = 1;		// in DHT and DQT the table type is one byte long
+const TUint KEntriesInQTable = 64;	// number of entries in a specific quantization table
+
+const TUint KDCTableSize = 28;		// see Exif 2.2 specification
+const TUint KACTableSize = 178;		// see Exif 2.2 specification
+
+const TUint KDQTTypicalSize = (KTableTypeSize + KEntriesInQTable) * 3;
+const TUint KDHTTypicalSize = (KTableTypeSize + KDCTableSize) * 2 + (KTableTypeSize + KACTableSize) * 2;
+
+const TInt KJpegBlockTypeUndefined = -1;
+
+_LIT(KDhtBlockHeader, "\xff\xc4\x00\x00");	// jpeg DHT block id followed by placeholder for block size
+_LIT(KDqtBlockHeader, "\xff\xdb\x00\x00");	// jpeg DQT block id followed by placeholder for block size
+	
+CExifEditUtility* CExifEditUtility::NewL(MExifSource* aSource, MExifDest* aDest, TBool aIgnoreExifMetadataProcessing)
+	{
+	CExifEditUtility * self= new (ELeave) CExifEditUtility(aIgnoreExifMetadataProcessing);
+	CleanupStack::PushL(self);
+	self->ConstructL(aSource, aDest);
+	CleanupStack::Pop(self);
+	
+	return self;
+	}
+	
+CExifEditUtility::~CExifEditUtility()
+	{
+	Cancel();
+	delete iScaledJpegGenerator;
+	delete iExifData;
+	iDHTTables.Close();
+	iDQTTables.Close();	
+	iTempCopyBuffer.Close();
+	}
+	
+CExifEditUtility::CExifEditUtility(TBool aIgnoreExifMetadataProcessing)
+	: CActive(EPriorityStandard)
+	, iState(EEmpty)
+	, iIgnoreExifMetadataProcessing(aIgnoreExifMetadataProcessing)
+	{
+	iLookupSlackBuff.SetLength( 0 );
+	}
+	
+void CExifEditUtility::ConstructL(MExifSource* aSource, MExifDest* aDest)
+	{
+	ASSERT(aSource != NULL);
+	ASSERT(aDest != NULL);
+	iSource = aSource;
+	iDest = aDest;
+	CActiveScheduler::Add(this);
+	}
+	
+
+MExifMetadata* CExifEditUtility::ExifMetadata()
+	{
+	return iExifData;
+	}
+
+
+//
+//
+//
+//
+//		interface methods
+//	
+
+void CExifEditUtility::ReadSourceL()
+	{
+	if(iState!=EEmpty)
+		{
+		User::Leave(KErrInUse);
+		}
+	
+	InitReadL();
+	if(!iIgnoreExifMetadataProcessing)
+		{
+		delete iExifData;
+		iExifData = NULL;
+		TRAPD(err, iExifData = iSource->ReadAndConvertExifDataL(iHeaderBlockSize + 
+			(sizeof(KJpgSOISignature)+sizeof(KJpgMarker)) /*size of the app1 marker*/, 
+			iExifBlockSize));
+		if(err != KErrNone)
+			{
+			if(err == KErrNoMemory)
+				{
+				User::Leave(KErrNoMemory);					
+				}
+			iIgnoreExifMetadataProcessing = ETrue;
+			iExifBlockSize = 0;
+			iConvertToJfif = ETrue;				
+			}
+
+		}
+	iState=EReadComplete;
+	}
+	
+
+void CExifEditUtility::WriteDestL(TBool aEncodeThumbnail, const TSize& aSize, TBool aPreserveImage, TBool aMaintainAspectRatio, TRequestStatus* aNotifier)
+	{
+	iNotifier=aNotifier;
+	iEncodeThumbnail=aEncodeThumbnail;
+	iSize= aSize;
+	iPreserveImage= aPreserveImage;
+	iMaintainAspectRatio = aMaintainAspectRatio;
+	if(iState !=EReadComplete)
+		{
+		RunError(KErrInUse);
+		return;
+		}
+	
+	TInt err=iDest->Init();
+	if(err!=KErrNone)
+		{
+		RunError(err);
+		return;
+		}
+
+	iState=EStartWrite;
+	ProcessCommandL();
+	*iNotifier=KRequestPending;
+	}
+
+
+//
+//		interface methods
+//	
+//
+//
+//
+
+
+
+
+
+
+//
+//
+//
+//
+//		methods from CActive
+//
+void CExifEditUtility::DoCancel()
+	{
+	iState=ECancelled;
+		
+	if(iScaledJpegGenerator)
+		{
+		iScaledJpegGenerator->Cancel();
+		}
+		
+	//Now reset everything to how it was before the transform.
+	iState = EReadComplete;	
+	iSource->Cancel();
+	iDest->Cancel();
+			
+	//We need to cancel both the internal state machine request status
+	//and the client request status	
+	if (iStatus == KRequestPending)
+		{
+		TRequestStatus* status=&iStatus;
+		User::RequestComplete(status, KErrCancel);
+		}
+	if (iNotifier)//but we do need to check iNotifier TRequestStatus
+		{
+		if (*iNotifier == KRequestPending)
+			{
+			User::RequestComplete(iNotifier,KErrCancel);
+			}
+		}
+	}
+
+
+void CExifEditUtility::RunL()
+	{
+	if(iStatus.Int()==KErrNone)
+		{
+		ProcessCommandL();
+		}
+	else
+		{
+		RunError(iStatus.Int());
+		}
+	}
+
+TInt CExifEditUtility::RunError(TInt aError)
+	{					
+	if(iState<EReadComplete)
+		{
+		delete iExifData;
+		iExifData = NULL;
+		iState=EEmpty;
+		iSource->CleanupAfterEarlyError();
+		iDest->CleanupAfterEarlyError();
+		}
+	else 
+		{
+		iState=EReadComplete;
+		iSource->CleanupAfterLateError();
+		iDest->CleanupAfterLateError();
+		}
+		
+	User::RequestComplete(iNotifier, aError);
+	return KErrNone;
+	}
+//
+//		interface methods
+//	
+//
+//
+//
+
+
+void CExifEditUtility::ProcessCommandL()
+	{
+	TBool doContinue=EFalse;
+	switch(iState)
+		{
+		// writer commands
+		case EStartWrite:
+			doContinue=DoCopyBlockL(0, iHeaderBlockSize, EWriteWriteHeader);
+			iState=ECopying;
+			break;
+		case EWriteWriteHeader:
+			if(!iIgnoreExifMetadataProcessing)
+				{
+				doContinue=DoWriteReadThumbnailL();
+				}
+			else
+				{
+				//skip to next step.
+				iStatus=KRequestPending;
+				TRequestStatus* reqStat=&iStatus;
+				User::RequestComplete(reqStat, KErrNone);
+				doContinue = ETrue; 
+				}
+			iState = EReadThumbnail;
+			break;
+		case EReadThumbnail:
+			doContinue=DoReadThumbnailL();		
+			if (iPreserveImage)
+				{
+				iState=EWriteReadThumbnail;
+				}
+			else
+				{
+				iState=EWriteWriteExif;
+				}
+			break;		
+		case EWriteReadThumbnail:
+			doContinue=DoWriteConvertExifL();
+			iState=EWriteConvertExif;
+			break;
+		case EWriteConvertExif:
+			if(iIgnoreExifMetadataProcessing && (iExifBlockSize >0))
+				{
+				HBufC8* destBuffer = iIOBufferPtr.AllocL();
+				iDest->SetDestBuffer(destBuffer); // Give iDest ownership of buffer
+				}
+			doContinue=DoWriteDestBufferL();
+			
+			if(iPreserveImage)
+				{
+				iState=EWriteWriteExif;
+				}
+			else
+				{
+				iState=EWriteReadMainImage;
+				}
+			break;
+		case EWriteWriteExif:
+			if(iPreserveImage)
+				{
+				doContinue=DoCopyBlockL(iTrailerOffset, iTrailerBlockSize, EWriteTrailer, iDestIsExif);
+				iState=ECopying;
+				}
+			else
+				{
+				doContinue=DoCreateMainImageL();
+				iCurrentWriteBufferIndex = 0;
+				iState=EWriteReadThumbnail;
+				}
+			break;
+		case EWriteTrailer:
+			DoWriteComplete();
+			iState=EReadComplete;
+			break;
+		case EWriteReadMainImage:
+			doContinue=DoWriteScaledImageL();
+			break;
+		case ECancelled:
+			return;
+		case ECopying:
+			iState=DoCopyNextBlockL();
+			doContinue=ETrue;
+			break;
+		default:
+			break;
+		}
+		
+	if(doContinue)
+		{
+		SetActive();
+		}
+	}
+
+
+
+
+//
+//
+//
+//
+//		Readers and writers
+//	each performs one step of the process
+
+
+//
+//			Readers
+//
+void CExifEditUtility::InitReadL()
+	{
+	iSource->InitL();
+	
+	TBool exifFound=EFalse;
+	
+	TUint16 jpegMarker=0;
+	TUint position=0;
+	TUint16 blockSize=0;
+	// we want to preserve the image data.
+	// so we parse the source file, and isolate the exif metadata
+	for(;;)
+		{		
+		position+=blockSize;
+		jpegMarker=KJpgSOISignature;
+		User::LeaveIfError(iSource->SetReadPosition(position));
+		do
+			{
+			jpegMarker=iSource->ReadUint16L();	
+			position+=sizeof(TUint16);
+			}while(jpegMarker==KJpgSOISignature);
+
+		if(jpegMarker==KJpgApp0Signature || jpegMarker==KJpgApp1Signature)
+			{
+			blockSize=iSource->ReadUint16L();
+			position+=sizeof(TUint16);
+			if (jpegMarker==KJpgApp1Signature)
+				{
+				exifFound=ETrue;
+				break;					
+				}
+			}
+		else
+			{
+			break;
+			}
+		}
+
+	User::LeaveIfError(iSource->Size(iTrailerBlockSize));
+	iTrailerOffset=sizeof(KJpgSOISignature);
+	if(exifFound)
+		{
+		// Set our reading parameters
+		TUint sizeOfApp1Marker = sizeof(KJpgSOISignature)+sizeof(KJpgMarker);
+		if(position <= sizeOfApp1Marker) User::Leave(KErrCorrupt);//source file is corrupted.
+		iHeaderBlockSize=position - sizeOfApp1Marker;	
+		iExifBlockSize=blockSize;
+		iTrailerOffset+=iHeaderBlockSize+blockSize;
+		}
+	else
+		{
+		iHeaderBlockSize=sizeof(KJpgSOISignature); 		
+		iExifBlockSize=0;
+		}
+	iTrailerBlockSize-= iTrailerOffset;
+	}
+
+//			Readers
+//
+//
+
+
+//
+//			Writers
+//
+TBool CExifEditUtility::DoWriteConvertExifL()
+	{
+	if(iIgnoreExifMetadataProcessing)
+		{
+		if(iExifBlockSize)
+			{
+			//copy the EXIF metadata en-block, without parsing it first.
+			iSource->ReadL(iHeaderBlockSize, iIOBufferPtr, iExifBlockSize + KBlockSizeLength, iStatus);
+			iDestIsExif = ETrue;
+			}
+		else
+			{ // JUMP to next step... no EXIF metadata information, so nothing to ignore.
+			if (iConvertToJfif)
+				{
+				//Create a buffer to hold a default JFIF header
+				HBufC8* buffer=HBufC8::NewMaxL(KJfifApp0DataSize+sizeof(KJpgApp0Signature));
+				TPtr8 buf(buffer->Des());
+				TJpegUtilities::CreateJfifHeader(buf);
+				iDest->SetDestBuffer(buffer);// Give iDest ownership of buffer				
+				}
+
+			iStatus=KRequestPending;	
+			TRequestStatus* reqStat=&iStatus;
+			User::RequestComplete(reqStat, KErrNone);
+			iDestIsExif = EFalse;
+			}
+		}
+	else
+		{	
+		TInt err = KErrNone;
+		if(iExifBlockSize > 0 || iExifData->IsExifDataModified())
+			{
+			HBufC8* buffer = NULL;
+			
+			UpdateImageSizeTagsL();
+			iExifData->CheckUpdateMandatoryTagsL();
+			
+			err= iExifData->CreateExifChunk(buffer); // buffer not owned by iExifData
+			if (err == KErrNone)
+				{
+				iDest->SetDestBuffer(buffer); // Give iDest ownership of buffer
+				}	
+			iExifData->ResetExifDataModified();
+			iDestIsExif = ETrue;	
+			}
+		else
+			{
+			iDestIsExif = EFalse;
+			}
+		iStatus=KRequestPending;	
+		TRequestStatus* reqStat=&iStatus;
+		User::RequestComplete(reqStat, err);	
+		}
+	return ETrue;	//we continue to the next step
+	}
+
+TBool CExifEditUtility::DoWriteReadThumbnailL()
+	{
+	if(iEncodeThumbnail)
+		{
+		HBufC8* buffer = iExifData->GetJpegThumbnailData(); // Does not transfer ownership of buffer
+		if(buffer==NULL)
+			{
+			delete iScaledJpegGenerator;
+			iScaledJpegGenerator=NULL;
+			// we have to generate the thumbnail
+			iScaledJpegGenerator=CScaledJpegGenerator::NewL(&iStatus, iSource);
+			iScaledJpegGenerator->StartL(TSize(KThumbnailWidth, KThumbnailHeight), CScaledJpegGenerator::EThumbnail);
+			}
+		else
+			{
+			iStatus=KRequestPending;
+			TRequestStatus* reqStat=&iStatus;
+			User::RequestComplete(reqStat, KErrNone);
+			}
+		}
+	else
+		{
+		iExifData->SetThumbnailData(NULL);
+		iStatus=KRequestPending;
+		TRequestStatus* reqStat=&iStatus;
+		User::RequestComplete(reqStat, KErrNone);
+		}
+
+	return ETrue;
+	}
+
+TBool CExifEditUtility::DoReadThumbnailL()
+	{
+	
+	if(iEncodeThumbnail && iScaledJpegGenerator)
+		{
+		HBufC8* thumb=iScaledJpegGenerator->GetJpegDataL(); // thumb not owned by iScaledJpegGenerator
+		delete iScaledJpegGenerator;
+		iScaledJpegGenerator=NULL;
+		iExifData->SetThumbnailData(thumb); // Give iExifData ownership of thumb
+		}
+	iStatus=KRequestPending;	
+	TRequestStatus* reqStat=&iStatus;
+	User::RequestComplete(reqStat, KErrNone);
+	return ETrue;
+	}
+
+TBool CExifEditUtility::DoCreateMainImageL()
+	{
+	if(!iScaledJpegGenerator)
+		{
+		iScaledJpegGenerator=CScaledJpegGenerator::NewL(&iStatus, iSource, iMaintainAspectRatio, 
+			CImageDecoder::EOptionIgnoreExifMetaData);
+		}
+	iScaledJpegGenerator->StartL(iSize, CScaledJpegGenerator::EMainImage);
+	return ETrue;
+	}
+
+TBool CExifEditUtility::DoWriteScaledImageL()
+	{
+	HBufC8* buffer = iScaledJpegGenerator->JpegDataBufferL(iCurrentWriteBufferIndex);
+	if (buffer)
+		{
+		iCurrentWriteBufferIndex++;
+		iState = EWriteReadMainImage;
+		}
+	else
+		{
+		iState = EWriteTrailer;
+		}
+	iDest->SetDestBuffer(buffer); // Give iDest ownership of buffer
+	return DoWriteDestBufferL();
+	}
+
+void CExifEditUtility::SetUpTablesL()
+	{
+	// set up the descriptors that store the combined tables
+
+	iDHTTables.Zero();
+	if (iDHTTables.MaxLength() < KDHTTypicalSize)
+		{
+		iDHTTables.ReAllocL(KDHTTypicalSize);
+		}
+	iDHTTables.Append(KDhtBlockHeader);	// write the DHT block id and placeholder size
+	
+	iDQTTables.Zero();
+	if (iDQTTables.MaxLength() < KDQTTypicalSize)
+		{
+		iDQTTables.ReAllocL(KDQTTypicalSize);
+		}
+	iDQTTables.Append(KDqtBlockHeader);	// write the DQT block id and placeholder size
+	}
+
+void CExifEditUtility::AppendTableDataL(RBuf8& aTables, const TDesC8& aData)
+	{
+	if (aTables.Length() + aData.Length() > aTables.MaxLength())
+		{
+		aTables.ReAllocL(aTables.Length() + aData.Length());
+		}
+	aTables.Append(aData);
+	}
+	
+TBool CExifEditUtility::DoCopyBlockL(TUint aStart, TInt aLength, TConvertState aNextStep, TBool aCombineTables)
+	{
+	iCurrentJpegBlockType = KJpegBlockTypeUndefined;
+	iCurrentJpegBlockSize = 0;
+
+	TUint bytesToRead = 0;
+	if (aCombineTables)
+		{
+		// EXIF only supports one DHT table and one DQT table (JFIF supports multiple tables)
+		// When copying the data, keep track of jpeg blocks and store any DHT and DQT tables
+		// Write the combined tables out before the SOS block
+
+		SetUpTablesL();
+
+		iCurCopyState = EReadJpegBlockInfo;
+		bytesToRead = KBlockIdAndSizeLength;
+		}
+	else
+		{
+		// copy the data in multiples of KIOBlockSize
+		iCurCopyState = ERead;				
+		bytesToRead = Min(aLength, KIOBlockSize);
+		}
+
+	iSource->ReadL(aStart, iIOBufferPtr, bytesToRead, iStatus);
+	iNextStep = aNextStep;
+	iCopyLength = aLength - bytesToRead;
+	return ETrue;	//we continue to the next step
+	}
+
+void CExifEditUtility::DoReadL()
+	{
+	TUint bytesToRead = 0;
+	if (iCurrentJpegBlockType == KJpegBlockTypeUndefined)
+		{
+		// start/continue reading data without tracking jpeg block info
+		iCurCopyState = ERead;
+		bytesToRead = Min(iCopyLength, KIOBlockSize);
+		}
+	else if (iCurrentJpegBlockSize == 0)
+		{
+		// read the next jpeg block's info
+		iCurCopyState = EReadJpegBlockInfo;
+		bytesToRead = KBlockIdAndSizeLength - iLookupSlackBuff.Length();
+		}
+	else
+		{
+		// start/continue reading data from this jpeg block
+		iCurCopyState = ERead;
+		bytesToRead = Min(iCurrentJpegBlockSize, KIOBlockSize);
+		iCurrentJpegBlockSize -= bytesToRead;	// keep track of how much block data is left to read 
+		}
+	iSource->NextReadL(iIOBufferPtr, bytesToRead, iStatus);
+	iCopyLength -= bytesToRead;	
+	}
+
+void CExifEditUtility::HandleJpegBlockL(const TDesC8& aPrependedData)
+	{
+	switch (iCurrentJpegBlockType)
+		{
+	case KDhtMarkerByte:
+	case KDqtMarkerByte:
+		DoReadL();
+		break;
+	case KSosMarkerByte:
+		{
+		// reached the SOS block so write out the tables
+		
+		// calculate the size of the combined DHT table data
+		TInt totalSize = iDHTTables.Length() - KBlockIdLength;
+		
+		// fill in the size placeholder
+		iDHTTables[2] = totalSize >> 8;
+		iDHTTables[3] = totalSize & 0xff;
+		
+		AppendTableDataL(iDHTTables, iIOBufferPtr);
+
+		// calculate the size of the combined DQT table data
+		totalSize = iDQTTables.Length() - KBlockIdLength;
+
+		// fill in the size placeholder
+		iDQTTables[2] = totalSize >> 8;
+		iDQTTables[3] = totalSize & 0xff;
+		
+		AppendTableDataL(iDQTTables, iDHTTables);
+
+		iDest->WriteL(iDQTTables, iDQTTables.Length(), iStatus);
+		break;
+		}
+    case (KJpgCommentSignature & 0xFF):
+	case (KJpgApp0Signature & 0xFF):
+        if (iDestIsExif)
+            {
+            // According to the EXIF 2.2 spec. we can't have comments
+            // and shouldn't have APP0, so skip them.
+			// APP0 can be skipped entirely using the block length.
+			// In case of comment block, crawl slowly, as the comment blocks may be corrupted
+			if(iCurrentJpegBlockType == (KJpgApp0Signature & 0xFF))
+            	{
+	            TInt fileSize = 0;
+				User::LeaveIfError(iSource->Size(fileSize));
+				ASSERT(fileSize > 0);
+	            TUint positionAfterCurBlock = fileSize - iCopyLength + iCurrentJpegBlockSize;
+	            if(positionAfterCurBlock > fileSize)
+	            	{
+	            	User::Leave(KErrCorrupt);
+	            	}
+				User::LeaveIfError(iSource->SetReadPosition(positionAfterCurBlock));            	
+            	}
+            iCurrentJpegBlockType = 0;
+            iCurrentJpegBlockSize = 0;
+            DoReadL();
+            break;	
+            }	
+            // if we'are not in EXIF mode fall to default block handling
+	default:
+	    {
+ 	    TInt len = aPrependedData.Length() +  iIOBufferPtr.Length();
+ 	    if (iTempCopyBuffer.MaxLength() < len)
+ 	    	{
+ 	    	iTempCopyBuffer.ReAllocL(len);
+ 	    	}
+ 	    iTempCopyBuffer = aPrependedData;
+        iTempCopyBuffer.Append( iIOBufferPtr );
+        iDest->WriteL(iTempCopyBuffer, len, iStatus);
+		break;
+		}
+	    } // switch
+	}
+
+TConvertState CExifEditUtility::DoCopyNextBlockL()
+	{
+	TConvertState result = ECopying;
+	
+	switch (iCurCopyState)
+		{
+	case EReadJpegBlockInfo:
+		{
+		iCurCopyState = EWrite;
+
+		// iIOBuffer should contain in order:
+		// - the jpeg marker byte (0xff)
+		// - the block id byte
+		// - the high byte of the block size
+		// - the low byte of the block size
+		const TText8* dataPtr = iIOBufferPtr.Ptr();
+		const TText8* const dataPtrLimit = dataPtr + iIOBufferPtr.Length();
+		if (iLookupSlackBuff.Length()==0 ) 
+		    {
+    		while (dataPtr < dataPtrLimit && *dataPtr != KJpegMarkerByte)
+    		    {
+    		    dataPtr++;
+    		    }
+    		const TInt dataLeft = TUint(dataPtrLimit - dataPtr);
+    		ASSERT(dataLeft >= 0 );
+    		if (dataLeft < KBlockIdAndSizeLength) 
+    		    {
+    		    if (dataLeft > 0) // some partial marker, take a copy of that data
+    		        {
+    		        iLookupSlackBuff.Copy(dataPtr, dataLeft);
+    		        }
+    		    DoReadL();   
+    		    break;
+    		    }
+
+    		// we should have some marker and block length in the buffer
+		    iCurrentJpegBlockType = dataPtr[1];
+		    iCurrentJpegBlockSize = (dataPtr[2] << 8) + dataPtr[3] - KBlockSizeLength;    		    
+		    }
+		else    // we have some marker in a slack buffer, so combine it with current data
+		    {
+		    if (iLookupSlackBuff.Length() + iIOBufferPtr.Length() < KBlockIdAndSizeLength)
+		        {
+		        User::Leave(KErrCorrupt); // unexpected and of image data
+		        }
+		    TInt needToAppend = KBlockIdAndSizeLength - iLookupSlackBuff.Length();
+		    iLookupSlackBuff.Append( iIOBufferPtr.Left( needToAppend ) );
+		    iIOBufferPtr.Shift( needToAppend );
+		    iCurrentJpegBlockType = iLookupSlackBuff[1];
+		    iCurrentJpegBlockSize = (iLookupSlackBuff[2] << 8) + iLookupSlackBuff[3] - KBlockSizeLength;    		    		    
+		    }
+		//check validity of the block size
+		if(iCurrentJpegBlockSize > iCopyLength)        
+			{        
+			User::Leave(KErrCorrupt);        
+			}        
+
+		HandleJpegBlockL(iLookupSlackBuff);
+        iLookupSlackBuff.SetLength( 0 );
+		break;
+		}
+	case ERead:
+		{
+		iCurCopyState = EWrite;
+
+		switch (iCurrentJpegBlockType)
+			{
+		case KDhtMarkerByte:
+			// store the DHT block data
+			AppendTableDataL(iDHTTables, iIOBufferPtr);
+			DoReadL();
+			break;
+		case KDqtMarkerByte:
+			// store the DQT block data
+			AppendTableDataL(iDQTTables, iIOBufferPtr);
+			DoReadL();
+			break;
+		default:
+			iDest->WriteL(iIOBufferPtr, iIOBufferPtr.Length(), iStatus);
+			ASSERT(iCopyLength >= 0);
+			if (iCopyLength == 0)
+				{
+				result = iNextStep;
+				}
+			break;
+			}
+		
+		break;
+		}
+	case EWrite:
+		{
+		if (iCurrentJpegBlockType == KSosMarkerByte)
+			{
+			iCurrentJpegBlockType = KJpegBlockTypeUndefined;	// tables have been written out, no need to track the jpeg blocks now
+			}
+		DoReadL();
+		break;
+		}
+	default:
+		break;
+		}
+
+	return result;
+	}
+
+
+TBool CExifEditUtility::DoWriteComplete()
+	{
+	iDest->WriteComplete();
+	
+	User::RequestComplete(iNotifier, KErrNone);
+	return EFalse;	
+	}
+
+
+TBool CExifEditUtility::DoWriteDestBufferL()
+	{
+	iDest->WriteDestBufferL(iStatus);
+	return ETrue;
+	}
+//			Writers
+//
+//
+
+//
+//		Readers and writers
+//	each performs one step of the process
+//
+//
+//
+
+	
+void CExifEditUtility::UpdateImageSizeTagsL()
+	{
+	if (iPreserveImage)
+		{
+		if (!iExifData->CheckImageSizeTags())
+			{
+			TSize destSize;
+			// In this case, jpeg data blocks are copied from source to dest, 
+			// so we need CImageDecoder to get the size of the image.
+			// NOTE: normally image size would be copied from valid Exif source 
+			// but this is for the case where that data is missing from the source.
+			CImageDecoder* decoder = NULL;
+			TRAPD(err, decoder = iSource->CreateImageDecoderL(CImageDecoder::EOptionNone));
+			if (err != KErrNone)
+				{
+				destSize = iSize;
+				}
+			else
+				{
+				destSize = decoder->FrameInfo().iFrameCoordsInPixels.Size();
+				delete decoder;
+				}
+			iExifData->UpdateImageSizeTagsL(destSize);
+			}
+		}
+	else
+		{
+		TSize destSize;
+		if (iScaledJpegGenerator) 
+			{
+			iScaledJpegGenerator->GetScaledImageSize(destSize);
+			}
+		else
+			{
+			destSize = iSize;
+			}
+		iExifData->UpdateImageSizeTagsL(destSize);
+		}
+	}
+
+