diff -r 000000000000 -r 5752a19fdefe imaging/imagingplugins/codecs/JPEGCodec/Exif/exifreaderwriter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/imaging/imagingplugins/codecs/JPEGCodec/Exif/exifreaderwriter.cpp Wed Aug 25 12:29:52 2010 +0300 @@ -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 +#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(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& aParam) + { + TInt err = DoSetIntegerArrayParam(aTag, aIfd, aParam); + if(err == KErrNone) + { + iExifDataModified = ETrue; + } + return err; + } + +TInt CExifReaderWriter::SetRationalArrayParam(TUint aTag, TUint aIfd, CArrayFix& aParam) + { + TInt err = DoSetRationalArrayParam(aTag, aIfd, aParam); + if(err == KErrNone) + { + iExifDataModified = ETrue; + } + return err; + } + +TInt CExifReaderWriter::SetShortArrayParam(TUint aTag, TUint aIfd, CArrayFix& 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& 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& 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& 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& 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& 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& 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; iIfd()==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; + }