--- /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;
+ }