Blob Blame History Raw
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
// of the Adobe license agreement accompanying it.
// =================================================================================================

#include "public/include/XMP_Environment.h"	// ! This must be the first include.
#include "public/include/XMP_Const.h"
#include "public/include/XMP_IO.hpp"

#include "XMPFiles/source/XMPFiles_Impl.hpp"
#include "source/XIO.hpp"

#include "XMPFiles/source/FileHandlers/JPEG_Handler.hpp"

#include "XMPFiles/source/FormatSupport/TIFF_Support.hpp"
#include "XMPFiles/source/FormatSupport/PSIR_Support.hpp"
#include "XMPFiles/source/FormatSupport/IPTC_Support.hpp"
#include "XMPFiles/source/FormatSupport/ReconcileLegacy.hpp"
#include "XMPFiles/source/FormatSupport/Reconcile_Impl.hpp"

#include "third-party/zuid/interfaces/MD5.h"

using namespace std;

// =================================================================================================
/// \file JPEG_Handler.cpp
/// \brief File format handler for JPEG.
///
/// This handler ...
///
// =================================================================================================

static const char * kExifSignatureString = "Exif\0\x00";	// There are supposed to be two zero bytes,
static const char * kExifSignatureAltStr = "Exif\0\xFF";	// but files have been seen with just one.
static const size_t kExifSignatureLength = 6;
static const size_t kExifMaxDataLength   = 0xFFFF - 2 - kExifSignatureLength;

static const char * kPSIRSignatureString = "Photoshop 3.0\0";
static const size_t kPSIRSignatureLength = 14;
static const size_t kPSIRMaxDataLength   = 0xFFFF - 2 - kPSIRSignatureLength;

static const char * kMainXMPSignatureString = "http://ns.adobe.com/xap/1.0/\0";
static const size_t kMainXMPSignatureLength = 29;

static const char * kExtXMPSignatureString = "http://ns.adobe.com/xmp/extension/\0";
static const size_t kExtXMPSignatureLength = 35;
static const size_t kExtXMPPrefixLength    = kExtXMPSignatureLength + 32 + 4 + 4;

typedef std::map < XMP_Uns32 /* offset */, std::string /* portion */ > ExtXMPPortions;

struct ExtXMPContent {
	XMP_Uns32 length;
	ExtXMPPortions portions;
	ExtXMPContent() : length(0) {};
	ExtXMPContent ( XMP_Uns32 _length ) : length(_length) {};
};

typedef std::map < JPEG_MetaHandler::GUID_32 /* guid */, ExtXMPContent /* content */ > ExtendedXMPInfo;

#ifndef Trace_UnlimitedJPEG
	#define Trace_UnlimitedJPEG 0
#endif

// =================================================================================================
// JPEG_MetaHandlerCTor
// ====================

XMPFileHandler * JPEG_MetaHandlerCTor ( XMPFiles * parent )
{
	return new JPEG_MetaHandler ( parent );

}	// JPEG_MetaHandlerCTor

// =================================================================================================
// JPEG_CheckFormat
// ================

// For JPEG we just check for the initial SOI standalone marker followed by any of the other markers
// that might, well, follow it. A more aggressive check might be to read 4KB then check for legit
// marker segments within that portion. Probably won't buy much, and thrashes the dCache more. We
// tolerate only a small amount of 0xFF padding between the SOI and following marker. This formally
// violates the rules of JPEG, but in practice there won't be any padding anyway.
//
// ! The CheckXyzFormat routines don't track the filePos, that is left to ScanXyzFile.

bool JPEG_CheckFormat ( XMP_FileFormat format,
	                    XMP_StringPtr  filePath,
                        XMP_IO *       fileRef,
                        XMPFiles *     parent )
{
	IgnoreParam(format); IgnoreParam(filePath); IgnoreParam(parent);
	XMP_Assert ( format == kXMP_JPEGFile );

	XMP_Uns8 buffer [100];
	XMP_Uns16 marker;

	fileRef->Rewind();
	if ( fileRef->Length() < 2 ) return false;	// Need at least the SOI marker.
	size_t bufferLen = fileRef->Read ( buffer, sizeof(buffer) );

	marker = GetUns16BE ( &buffer[0] );
	if ( marker != 0xFFD8 ) return false;	// Offset 0 must have the SOI marker.
	
	// Skip 0xFF padding and high order 0xFF of next marker.
	size_t bufferPos = 2;
	while ( (bufferPos < bufferLen) && (buffer[bufferPos] == 0xFF) ) bufferPos += 1;
	if ( bufferPos == bufferLen ) return true;	// Nothing but 0xFF bytes, close enough.

	XMP_Uns8 id = buffer[bufferPos];	// Check the ID of the second marker.
	if ( id >= 0xDD ) return true;	// The most probable cases.
	if ( (id < 0xC0) || ((id & 0xF8) == 0xD0) || (id == 0xD8) || (id == 0xDA) || (id == 0xDC) ) return false;
	return true;

}	// JPEG_CheckFormat

// =================================================================================================
// JPEG_MetaHandler::JPEG_MetaHandler
// ==================================

JPEG_MetaHandler::JPEG_MetaHandler ( XMPFiles * _parent )
	: exifMgr(0), psirMgr(0), iptcMgr(0), skipReconcile(false)
{
	this->parent = _parent;
	this->handlerFlags = kJPEG_HandlerFlags;
	this->stdCharForm  = kXMP_Char8Bit;

}	// JPEG_MetaHandler::JPEG_MetaHandler

// =================================================================================================
// JPEG_MetaHandler::~JPEG_MetaHandler
// ===================================

JPEG_MetaHandler::~JPEG_MetaHandler()
{

	if ( exifMgr != 0 ) delete ( exifMgr );
	if ( psirMgr != 0 ) delete ( psirMgr );
	if ( iptcMgr != 0 ) delete ( iptcMgr );

}	// JPEG_MetaHandler::~JPEG_MetaHandler

// =================================================================================================
// CacheExtendedXMP
// ================

static void CacheExtendedXMP ( ExtendedXMPInfo * extXMP, XMP_Uns8 * buffer, size_t bufferLen )
{

	// Have a portion of the extended XMP, cache the contents. This is complicated by the need to
	// tolerate files where the extension portions are not in order. The local ExtendedXMPInfo map
	// uses the GUID as the key and maps that to a struct that has the full length and a map of the
	// known portions. This known portion map uses the offset of the portion as the key and maps
	// that to a string. Only fully seen extended XMP streams are kept, the right one gets picked in
	// ProcessXMP.

	// The extended XMP JPEG marker segment content holds:
	//	- a signature string, "http://ns.adobe.com/xmp/extension/\0", already verified
	//	- a 128 bit GUID stored as a 32 byte ASCII hex string
	//	- a UInt32 full length of the entire extended XMP
	//	- a UInt32 offset for this portion of the extended XMP
	//	- the UTF-8 text for this portion of the extended XMP
	
	if ( bufferLen < kExtXMPPrefixLength ) return;	// Ignore bad input.
	XMP_Assert ( CheckBytes ( &buffer[0], kExtXMPSignatureString, kExtXMPSignatureLength ) );

	XMP_Uns8 * bufferPtr = buffer + kExtXMPSignatureLength;	// Start at the GUID.
	
	JPEG_MetaHandler::GUID_32 guid;
	XMP_Assert ( sizeof(guid.data) == 32 );
	memcpy ( &guid.data[0], bufferPtr, sizeof(guid.data) );	// AUDIT: Use of sizeof(guid.data) is safe.

	bufferPtr += sizeof(guid.data);	// Move to the length and offset.
	XMP_Uns32 fullLen = GetUns32BE ( bufferPtr );
	XMP_Uns32 offset  = GetUns32BE ( bufferPtr+4 );

	bufferPtr += 8;	// Move to the XMP stream portion.
	size_t xmpLen = bufferLen - kExtXMPPrefixLength;

	#if Trace_UnlimitedJPEG
		printf ( "New extended XMP portion: fullLen %d, offset %d, GUID %.32s\n", fullLen, offset, guid.data );
	#endif

	// Find the ExtXMPContent for this GUID, and the string for this portion's offset.

	ExtendedXMPInfo::iterator guidPos = extXMP->find ( guid );
	if ( guidPos == extXMP->end() ) {
		ExtXMPContent newExtContent ( fullLen );
		guidPos = extXMP->insert ( extXMP->begin(), ExtendedXMPInfo::value_type ( guid, newExtContent ) );
	}

	ExtXMPPortions::iterator offsetPos;
	ExtXMPContent & extContent = guidPos->second;

	if ( extContent.portions.empty() ) {
		// When new create a full size offset 0 string, to which all in-order portions will get appended.
		offsetPos = extContent.portions.insert ( extContent.portions.begin(),
												 ExtXMPPortions::value_type ( 0, std::string() ) );
		offsetPos->second.reserve ( extContent.length );
	}

	// Try to append this portion to a logically contiguous preceeding one.

	if ( offset == 0 ) {
		offsetPos = extContent.portions.begin();
		XMP_Assert ( (offsetPos->first == 0) && (offsetPos->second.size() == 0) );
	} else {
		offsetPos = extContent.portions.lower_bound ( offset );
		--offsetPos;	// Back up to the portion whose offset is less than the new offset.
		if ( (offsetPos->first + offsetPos->second.size()) != offset ) {
			// Can't append, create a new portion.
			offsetPos = extContent.portions.insert ( extContent.portions.begin(),
													 ExtXMPPortions::value_type ( offset, std::string() ) );
		}
	}

	// Cache this portion of the extended XMP.

	std::string & extPortion = offsetPos->second;
	extPortion.append ( (XMP_StringPtr)bufferPtr, xmpLen );

}	// CacheExtendedXMP

// =================================================================================================
// JPEG_MetaHandler::CacheFileData
// ===============================
//
// Look for the Exif metadata, Photoshop image resources, and XMP in a JPEG (JFIF) file. The native
// thumbnail is inside the Exif. The general layout of a JPEG file is:
//    SOI marker, 2 bytes, 0xFFD8
//    Marker segments for tables and metadata
//    SOFn marker segment
//    Image data
//    EOI marker, 2 bytes, 0xFFD9
//
// Each marker segment begins with a 2 byte big endian marker and a 2 byte big endian length. The
// length includes the 2 bytes of the length field but not the marker. The high order byte of a
// marker is 0xFF, the low order byte tells what kind of marker. A marker can be preceeded by any
// number of 0xFF fill bytes, however there are no alignment constraints.
//
// There are virtually no constraints on the order of the marker segments before the SOFn. A reader
// must be prepared to handle any order.
//
// The Exif metadata is in an APP1 marker segment with a 6 byte signature string of "Exif\0\0" at
// the start of the data. The rest of the data is a TIFF stream.
//
// The Photoshop image resources are in an APP13 marker segment with a 14 byte signature string of
// "Photoshop 3.0\0". The rest of the data is a sequence of image resources.
//
// The main XMP is in an APP1 marker segment with a 29 byte signature string of
// "http://ns.adobe.com/xap/1.0/\0". The rest of the data is the serialized XMP packet. This is the
// only XMP if everything fits within the 64KB limit for marker segment data. If not, there will be
// a series of XMP extension segments.
//
// Each XMP extension segment is an APP1 marker segment whose data contains:
//    - A 35 byte signature string of "http://ns.adobe.com/xmp/extension/\0".
//    - A 128 bit GUID stored as 32 ASCII hex digits, capital A-F, no nul termination.
//    - A 32 bit unsigned integer length for the full extended XMP serialization.
//    - A 32 bit unsigned integer offset for this portion of the extended XMP serialization.
//    - A portion of the extended XMP serialization, up to about 65400 bytes (at most 65458).
//
// A reader must be prepared to encounter the extended XMP portions out of order. Also to encounter
// defective files that have differing extended XMP according to the GUID. The main XMP contains the
// GUID for the associated extended XMP.

// *** This implementation simply returns when invalid JPEG is encountered. Should we throw instead?

void JPEG_MetaHandler::CacheFileData()
{
	XMP_IO* fileRef = this->parent->ioRef;
	XMP_PacketInfo & packetInfo = this->packetInfo;

	static const size_t kBufferSize = 64*1024;	// Enough for maximum segment contents.
	XMP_Uns8 buffer [kBufferSize];

	psirContents.clear();
	exifContents.clear();

	XMP_AbortProc abortProc  = this->parent->abortProc;
	void *        abortArg   = this->parent->abortArg;
	const bool    checkAbort = (abortProc != 0);

	ExtendedXMPInfo extXMP;

	XMP_Assert ( ! this->containsXMP );
	// Set containsXMP to true here only if the standard XMP packet is found.

	XMP_Assert ( kPSIRSignatureLength == (strlen(kPSIRSignatureString) + 1) );
	XMP_Assert ( kMainXMPSignatureLength == (strlen(kMainXMPSignatureString) + 1) );
	XMP_Assert ( kExtXMPSignatureLength == (strlen(kExtXMPSignatureString) + 1) );

	// -------------------------------------------------------------------------------------------
	// Look for any of the Exif, PSIR, main XMP, or extended XMP marker segments. Quit when we hit
	// an SOFn, EOI, or invalid/unexpected marker.

	fileRef->Seek ( 2, kXMP_SeekFromStart  );	// Skip the SOI, CheckFormat made sure it is present.

	while ( true ) {

		if ( checkAbort && abortProc(abortArg) ) {
			XMP_Throw ( "JPEG_MetaHandler::CacheFileData - User abort", kXMPErr_UserAbort );
		}

		if ( ! XIO::CheckFileSpace ( fileRef, 2 ) ) return;	// Quit, don't throw, if the file ends unexpectedly.
		
		XMP_Uns16 marker = XIO::ReadUns16_BE ( fileRef );	// Read the next marker.
		if ( marker == 0xFFFF ) {
			// Have a pad byte, skip it. These are almost unheard of, so efficiency isn't critical.
			fileRef->Seek ( -1, kXMP_SeekFromCurrent );	// Skip the first 0xFF, read the second again.
			continue;
		}

		if ( (marker == 0xFFDA) || (marker == 0xFFD9) ) break;	// Quit reading at the first SOS marker or at EOI.

		if ( (marker == 0xFF01) ||	// Ill-formed file if we encounter a TEM or RSTn marker.
			 ((0xFFD0 <= marker) && (marker <= 0xFFD7)) ) return;

		XMP_Uns16 contentLen = XIO::ReadUns16_BE ( fileRef );	// Read this segment's length.
		if ( contentLen < 2 ) XMP_Throw ( "Invalid JPEG segment length", kXMPErr_BadJPEG );
		contentLen -= 2;	// Reduce to just the content length.
		
		XMP_Int64 contentOrigin = fileRef->Offset();
		size_t signatureLen;

		if ( (marker == 0xFFED) && (contentLen >= kPSIRSignatureLength) ) {

			// This is an APP13 marker, is it the Photoshop image resources?

			signatureLen = fileRef->Read ( buffer, kPSIRSignatureLength );
			if ( (signatureLen == kPSIRSignatureLength) &&
				 CheckBytes ( &buffer[0], kPSIRSignatureString, kPSIRSignatureLength ) ) {

				size_t psirLen = contentLen - kPSIRSignatureLength;
				fileRef->Seek ( (contentOrigin + kPSIRSignatureLength), kXMP_SeekFromStart );
				fileRef->ReadAll ( buffer, psirLen );
				this->psirContents.append( (char *) buffer, psirLen );
				continue;	// Move on to the next marker.

			}

		} else if ( (marker == 0xFFE1) && (contentLen >= kExifSignatureLength) ) {	// Check for the shortest signature.

			// This is an APP1 marker, is it the Exif, main XMP, or extended XMP?
			// ! Check in that order, which is in increasing signature string length.
			
			XMP_Assert ( (kExifSignatureLength < kMainXMPSignatureLength) &&
						 (kMainXMPSignatureLength < kExtXMPSignatureLength) );
			signatureLen = fileRef->Read ( buffer, kExtXMPSignatureLength );	// Read for the longest signature.

			if ( (signatureLen >= kExifSignatureLength) &&
				 (CheckBytes ( &buffer[0], kExifSignatureString, kExifSignatureLength ) ||
				  CheckBytes ( &buffer[0], kExifSignatureAltStr, kExifSignatureLength )) ) {

				size_t exifLen = contentLen - kExifSignatureLength;
				fileRef->Seek ( (contentOrigin + kExifSignatureLength), kXMP_SeekFromStart );
				fileRef->ReadAll ( buffer, exifLen );
				this->exifContents.append ( (char*)buffer, exifLen );
				continue;	// Move on to the next marker.

			}
			
			if ( (signatureLen >= kMainXMPSignatureLength) &&
				 CheckBytes ( &buffer[0], kMainXMPSignatureString, kMainXMPSignatureLength ) ) {

				this->containsXMP = true;	// Found the standard XMP packet.
				size_t xmpLen = contentLen - kMainXMPSignatureLength;
				fileRef->Seek ( (contentOrigin + kMainXMPSignatureLength), kXMP_SeekFromStart );
				fileRef->ReadAll ( buffer, xmpLen );
				this->xmpPacket.assign ( (char*)buffer, xmpLen );
				this->packetInfo.offset = contentOrigin + kMainXMPSignatureLength;
				this->packetInfo.length = (XMP_Int32)xmpLen;
				this->packetInfo.padSize   = 0;	// Assume the rest for now, set later in ProcessXMP.
				this->packetInfo.charForm  = kXMP_CharUnknown;
				this->packetInfo.writeable = true;
				continue;	// Move on to the next marker.

			}
			
			if ( (signatureLen >= kExtXMPSignatureLength) &&
				 CheckBytes ( &buffer[0], kExtXMPSignatureString, kExtXMPSignatureLength ) ) {

				fileRef->Seek ( contentOrigin, kXMP_SeekFromStart );
				fileRef->ReadAll ( buffer, contentLen );
				CacheExtendedXMP ( &extXMP, buffer, contentLen );
				continue;	// Move on to the next marker.

			}

		}
		
		// None of the above, seek to the next marker.
		fileRef->Seek ( (contentOrigin + contentLen) , kXMP_SeekFromStart );

	}

	if ( ! extXMP.empty() ) {

		// We have extended XMP. Find out which ones are complete, collapse them into a single
		// string, and save them for ProcessXMP.

		ExtendedXMPInfo::iterator guidPos = extXMP.begin();
		ExtendedXMPInfo::iterator guidEnd = extXMP.end();

		for ( ; guidPos != guidEnd; ++guidPos ) {

			ExtXMPContent & thisContent = guidPos->second;
			ExtXMPPortions::iterator partZero = thisContent.portions.begin();
			ExtXMPPortions::iterator partEnd  = thisContent.portions.end();
			ExtXMPPortions::iterator partPos  = partZero;

			#if Trace_UnlimitedJPEG
				printf ( "Extended XMP portions for GUID %.32s, full length %d\n",
					     guidPos->first.data, guidPos->second.length );
				printf ( "  Offset %d, length %d, next offset %d\n",
						 partZero->first, partZero->second.size(), (partZero->first + partZero->second.size()) );
			#endif

			for ( ++partPos; partPos != partEnd; ++partPos ) {
				#if Trace_UnlimitedJPEG
					printf ( "  Offset %d, length %d, next offset %d\n",
							 partPos->first, partPos->second.size(), (partPos->first + partPos->second.size()) );
				#endif
				if ( partPos->first != partZero->second.size() ) break;	// Quit if not contiguous.
				partZero->second.append ( partPos->second );
			}

			if ( (partPos == partEnd) && (partZero->first == 0) && (partZero->second.size() == thisContent.length) ) {
				// This is a complete extended XMP stream.
				this->extendedXMP.insert ( ExtendedXMPMap::value_type ( guidPos->first, partZero->second ) );
				#if Trace_UnlimitedJPEG
					printf ( "Full extended XMP for GUID %.32s, full length %d\n",
							 guidPos->first.data, partZero->second.size() );
				#endif
			}

		}

	}

}	// JPEG_MetaHandler::CacheFileData

// =================================================================================================
// TrimFullExifAPP1
// ================
//
// Try to trim trailing padding from full Exif APP1 segment written by some Nikon cameras. Do a
// temporary read-only parse of the Exif APP1 contents, determine the highest used offset, trim the
// padding if all zero bytes.

static const char * IFDNames[] = { "Primary", "TNail", "Exif", "GPS", "Interop", };

static void TrimFullExifAPP1 ( std::string * exifContents )
{
	TIFF_MemoryReader tempMgr;
	TIFF_MemoryReader::TagInfo tagInfo;
	bool tagFound, isNikon;

	// ! Make a copy of the data to parse! The RO memory TIFF manager will flip bytes in-place!
	tempMgr.ParseMemoryStream ( exifContents->data(), (XMP_Uns32)exifContents->size(), true /* copy data */ );

	// Only trim the Exif APP1 from Nikon cameras.
	tagFound = tempMgr.GetTag ( kTIFF_PrimaryIFD, kTIFF_Make, &tagInfo );
	isNikon = tagFound && (tagInfo.type == kTIFF_ASCIIType) && (tagInfo.count >= 5) &&
						  (memcmp ( tagInfo.dataPtr, "NIKON", 5) == 0);
	if ( ! isNikon ) return;

	// Find the start of the padding, one past the highest used offset. Look at the IFD structure,
	// and the thumbnail info. Ignore the MakerNote tag, Nikon says they are self-contained.

	XMP_Uns32 padOffset = 0;

	for ( XMP_Uns8 ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) {

		TIFF_MemoryReader::TagInfoMap tagMap;
		bool ifdFound = tempMgr.GetIFD ( ifd, &tagMap );
		if ( ! ifdFound ) continue;

		TIFF_MemoryReader::TagInfoMap::const_iterator mapPos = tagMap.begin();
		TIFF_MemoryReader::TagInfoMap::const_iterator mapEnd = tagMap.end();

		for ( ; mapPos != mapEnd; ++mapPos ) {
			const TIFF_MemoryReader::TagInfo & tagInfo = mapPos->second;
			XMP_Uns32 tagEnd = tempMgr.GetValueOffset ( ifd, tagInfo.id ) + tagInfo.dataLen;
			if ( tagEnd > padOffset ) padOffset = tagEnd;
		}

	}

	tagFound = tempMgr.GetTag ( kTIFF_TNailIFD, kTIFF_JPEGInterchangeFormat, &tagInfo );
	if ( tagFound ) {
		XMP_Uns32 tnailOffset = tempMgr.GetUns32 ( tagInfo.dataPtr );
		tagFound = tempMgr.GetTag ( kTIFF_TNailIFD, kTIFF_JPEGInterchangeFormatLength, &tagInfo );
		if ( ! tagFound ) return;	// Don't trim if there is a TNail offset but no length.
		tnailOffset += tempMgr.GetUns32 ( tagInfo.dataPtr );
		if ( tnailOffset > padOffset ) padOffset = tnailOffset;
	}

	// Decide if it is OK to trim the Exif segment. It is OK if the padding is all zeros. It is OK
	// if the last non-zero byte is no more than 64 bytes into the padding and there are at least
	// an additional 64 bytes of padding after it.
	
	if ( padOffset >= exifContents->size() ) return;	// Sanity check for an OK last used offset.

	size_t lastNonZero = exifContents->size() - 1;
	while ( (lastNonZero >= padOffset) && ((*exifContents)[lastNonZero] == 0) ) --lastNonZero;
	
	bool ok = lastNonZero < padOffset;
	if ( ! ok ) {
		size_t nzSize = lastNonZero - padOffset + 1;
		size_t finalSize = (exifContents->size() - 1) - lastNonZero;
		if ( (nzSize < 64) && (finalSize > 64) ) {
			padOffset = lastNonZero + 64;
			assert ( padOffset < exifContents->size() );
			ok = true;
		}
	}
	
	if ( ok ) exifContents->erase ( padOffset );

}	// TrimFullExifAPP1

// =================================================================================================
// JPEG_MetaHandler::ProcessXMP
// ============================
//
// Process the raw XMP and legacy metadata that was previously cached.

void JPEG_MetaHandler::ProcessXMP()
{

	XMP_Assert ( ! this->processedXMP );
	this->processedXMP = true;	// Make sure we only come through here once.

	// Create the PSIR and IPTC handlers, even if there is no legacy. They might be needed for updates.

	XMP_Assert ( (this->psirMgr == 0) && (this->iptcMgr == 0) );	// ProcessTNail might create the exifMgr.

	bool readOnly = false;
	if ( this->parent ){
		readOnly = ((this->parent->openFlags & kXMPFiles_OpenForUpdate) == 0);
	}
	if ( readOnly ) {
		if ( this->exifMgr == 0 ) this->exifMgr = new TIFF_MemoryReader();
		this->psirMgr = new PSIR_MemoryReader();
		this->iptcMgr = new IPTC_Reader();	// ! Parse it later.
	} else {
		if ( this->exifContents.size() == (65534 - 2 - 6) ) TrimFullExifAPP1 ( &this->exifContents );
		if ( this->exifMgr == 0 ) this->exifMgr = new TIFF_FileWriter();
		this->psirMgr = new PSIR_FileWriter();
		this->iptcMgr = new IPTC_Writer();	// ! Parse it later.
	}
	if ( this->parent )
		exifMgr->SetErrorCallback( &this->parent->errorCallback );

	// Set up everything for the legacy import, but don't do it yet. This lets us do a forced legacy
	// import if the XMP packet gets parsing errors.

	TIFF_Manager & exif = *this->exifMgr;	// Give the compiler help in recognizing non-aliases.
	PSIR_Manager & psir = *this->psirMgr;
	IPTC_Manager & iptc = *this->iptcMgr;

	bool haveExif = (! this->exifContents.empty());
	if ( haveExif ) {
		exif.ParseMemoryStream ( this->exifContents.c_str(), (XMP_Uns32)this->exifContents.size() );
	}

	bool havePSIR = (! this->psirContents.empty());
	if ( havePSIR ) {
		psir.ParseMemoryResources ( this->psirContents.c_str(), (XMP_Uns32)this->psirContents.size() );
	}

	PSIR_Manager::ImgRsrcInfo iptcInfo;
	bool haveIPTC = false;
	if ( havePSIR ) haveIPTC = psir.GetImgRsrc ( kPSIR_IPTC, &iptcInfo );;
	int iptcDigestState = kDigestMatches;

	if ( haveIPTC ) {

		bool haveDigest = false;
		PSIR_Manager::ImgRsrcInfo digestInfo;
		if ( havePSIR ) haveDigest = psir.GetImgRsrc ( kPSIR_IPTCDigest, &digestInfo );
		if ( digestInfo.dataLen != 16 ) haveDigest = false;

		if ( ! haveDigest ) {
			iptcDigestState = kDigestMissing;
		} else {
			iptcDigestState = PhotoDataUtils::CheckIPTCDigest ( iptcInfo.dataPtr, iptcInfo.dataLen, digestInfo.dataPtr );
		}

	}

	XMP_OptionBits options = 0;
	if ( this->containsXMP ) options |= k2XMP_FileHadXMP;
	if ( haveExif ) options |= k2XMP_FileHadExif;
	if ( haveIPTC ) options |= k2XMP_FileHadIPTC;

	// Process the main XMP packet. If it fails to parse, do a forced legacy import but still throw
	// an exception. This tells the caller that an error happened, but gives them recovered legacy
	// should they want to proceed with that.

	bool haveXMP = false;

	if ( ! this->xmpPacket.empty() ) {
		XMP_Assert ( this->containsXMP );
		// Common code takes care of packetInfo.charForm, .padSize, and .writeable.
		XMP_StringPtr packetStr = this->xmpPacket.c_str();
		XMP_StringLen packetLen = (XMP_StringLen)this->xmpPacket.size();
		try {
			this->xmpObj.ParseFromBuffer ( packetStr, packetLen );
		} catch ( ... ) { /* Ignore parsing failures, someday we hope to get partial XMP back. */ }
		haveXMP = true;
	}

	// Process the extended XMP if it has a matching GUID.

	if ( ! this->extendedXMP.empty() ) {

		bool found;
		GUID_32 g32;
		std::string extGUID, extPacket;
		ExtendedXMPMap::iterator guidPos = this->extendedXMP.end();

		found = this->xmpObj.GetProperty ( kXMP_NS_XMP_Note, "HasExtendedXMP", &extGUID, 0 );
		if ( found && (extGUID.size() == sizeof(g32.data)) ) {
			XMP_Assert ( sizeof(g32.data) == 32 );
			memcpy ( g32.data, extGUID.c_str(), sizeof(g32.data) );	// AUDIT: Use of sizeof(g32.data) is safe.
			guidPos = this->extendedXMP.find ( g32 );
			this->xmpObj.DeleteProperty ( kXMP_NS_XMP_Note, "HasExtendedXMP" );	// ! Must only be in the file.
			#if Trace_UnlimitedJPEG
				printf ( "%s extended XMP for GUID %s\n",
					     ((guidPos != this->extendedXMP.end()) ? "Found" : "Missing"), extGUID.c_str() );
			#endif
		}

		if ( guidPos != this->extendedXMP.end() ) {
			try {
				XMP_StringPtr extStr = guidPos->second.c_str();
				XMP_StringLen extLen = (XMP_StringLen)guidPos->second.size();
				SXMPMeta extXMP ( extStr, extLen );
				SXMPUtils::MergeFromJPEG ( &this->xmpObj, extXMP );
			} catch ( ... ) {
				// Ignore failures, let the rest of the XMP and legacy be kept.
			}
		}

	}

	// Process the legacy metadata.

	if ( haveIPTC && (! haveXMP) && (iptcDigestState == kDigestMatches) ) iptcDigestState = kDigestMissing;
	bool parseIPTC = (iptcDigestState != kDigestMatches) || (! readOnly);
	if ( parseIPTC ) iptc.ParseMemoryDataSets ( iptcInfo.dataPtr, iptcInfo.dataLen );
	ImportPhotoData ( exif, iptc, psir, iptcDigestState, &this->xmpObj, options );

	this->containsXMP = true;	// Assume we had something for the XMP.

}	// JPEG_MetaHandler::ProcessXMP

// =================================================================================================
// JPEG_MetaHandler::UpdateFile
// ============================

void JPEG_MetaHandler::UpdateFile ( bool doSafeUpdate )
{
	XMP_Assert ( ! doSafeUpdate );	// This should only be called for "unsafe" updates.

	XMP_Int64 oldPacketOffset = this->packetInfo.offset;
	XMP_Int32 oldPacketLength = this->packetInfo.length;

	if ( oldPacketOffset == kXMPFiles_UnknownOffset ) oldPacketOffset = 0;	// ! Simplify checks.
	if ( oldPacketLength == kXMPFiles_UnknownLength ) oldPacketLength = 0;

	bool fileHadXMP = ((oldPacketOffset != 0) && (oldPacketLength != 0));

	// Update the IPTC-IIM and native TIFF/Exif metadata. ExportPhotoData also trips the tiff: and
	// exif: copies from the XMP, so reserialize the now final XMP packet.

	ExportPhotoData ( kXMP_JPEGFile, &this->xmpObj, this->exifMgr, this->iptcMgr, this->psirMgr );

	try {
		XMP_OptionBits options = kXMP_UseCompactFormat;
		if ( fileHadXMP ) options |= kXMP_ExactPacketLength;
		this->xmpObj.SerializeToBuffer ( &this->xmpPacket, options, oldPacketLength );
	} catch ( ... ) {
		this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_UseCompactFormat );
	}

	// Decide whether to do an in-place update. This can only happen if all of the following are true:
	//	- There is a standard packet in the file.
	//	- There is no extended XMP in the file.
	//	- The are no changes to the legacy Exif or PSIR portions. (The IPTC is in the PSIR.)
	//	- The new XMP can fit in the old space, without extensions.

	bool doInPlace = (fileHadXMP && (this->xmpPacket.size() <= (size_t)oldPacketLength));

	if ( ! this->extendedXMP.empty() ) doInPlace = false;

	if ( (this->exifMgr != 0) && (this->exifMgr->IsLegacyChanged()) ) doInPlace = false;
	if ( (this->psirMgr != 0) && (this->psirMgr->IsLegacyChanged()) ) doInPlace = false;

	if ( doInPlace ) {

		#if GatherPerformanceData
			sAPIPerf->back().extraInfo += ", JPEG in-place update";
		#endif

		if ( this->xmpPacket.size() < (size_t)this->packetInfo.length ) {
			// They ought to match, cheap to be sure.
			size_t extraSpace = (size_t)this->packetInfo.length - this->xmpPacket.size();
			this->xmpPacket.append ( extraSpace, ' ' );
		}

		XMP_IO* liveFile = this->parent->ioRef;
		std::string & newPacket = this->xmpPacket;

		XMP_Assert ( newPacket.size() == (size_t)oldPacketLength );	// ! Done by common PutXMP logic.

		liveFile->Seek ( oldPacketOffset, kXMP_SeekFromStart  );
		liveFile->Write ( newPacket.c_str(), (XMP_Int32)newPacket.size() );

	} else {

		#if GatherPerformanceData
			sAPIPerf->back().extraInfo += ", JPEG copy update";
		#endif

		XMP_IO* origRef = this->parent->ioRef;
		XMP_IO* tempRef = origRef->DeriveTemp();

		try {
			XMP_Assert ( ! this->skipReconcile );
			this->skipReconcile = true;
			this->WriteTempFile ( tempRef );
			this->skipReconcile = false;
		} catch ( ... ) {
			this->skipReconcile = false;
			origRef->DeleteTemp();
			throw;
		}

		origRef->AbsorbTemp();

	}

	this->needsUpdate = false;

}	// JPEG_MetaHandler::UpdateFile

// =================================================================================================
// JPEG_MetaHandler::WriteTempFile
// ===============================
//
// The metadata parts of a JPEG file are APP1 marker segments for Exif and XMP, and an APP13 marker
// segment for Photoshop image resources which contain the IPTC. Corresponding marker segments in
// the source file are ignored, other parts of the source file are copied. Any initial APP0 marker
// segments are copied first. Then the new Exif, XMP, and PSIR marker segments are written. Then the
// rest of the file is copied, skipping the old Exif, XMP, and PSIR. The checking for old metadata
// stops at the first SOFn marker.

void JPEG_MetaHandler::WriteTempFile ( XMP_IO* tempRef )
{
	XMP_IO* origRef = this->parent->ioRef;

	XMP_AbortProc abortProc  = this->parent->abortProc;
	void *        abortArg   = this->parent->abortArg;
	const bool    checkAbort = (abortProc != 0);

	XMP_Uns16 marker, contentLen;

	static const size_t kBufferSize = 64*1024;	// Enough for a segment with maximum contents.
	XMP_Uns8 buffer [kBufferSize];
	
	XMP_Int64 origLength = origRef->Length();
	if ( origLength == 0 ) return;	// Tolerate empty files.
	if ( origLength < 4 ) {
		XMP_Throw ( "JPEG must have at least SOI and EOI markers", kXMPErr_BadJPEG );
	}

	if ( ! skipReconcile ) {
		// Update the IPTC-IIM and native TIFF/Exif metadata, and reserialize the now final XMP packet.
		ExportPhotoData ( kXMP_JPEGFile, &this->xmpObj, this->exifMgr, this->iptcMgr, this->psirMgr );
		this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_UseCompactFormat );
	}

	origRef->Rewind();
	tempRef->Truncate ( 0 );

	marker = XIO::ReadUns16_BE ( origRef );	// Just read the SOI marker.
	if ( marker != 0xFFD8 ) XMP_Throw ( "Missing SOI marker", kXMPErr_BadJPEG );
	XIO::WriteUns16_BE ( tempRef, marker );

	// Copy any leading APP0 marker segments.

	while ( true ) {

		if ( checkAbort && abortProc(abortArg) ) {
			XMP_Throw ( "JPEG_MetaHandler::WriteFile - User abort", kXMPErr_UserAbort );
		}
		
		if ( ! XIO::CheckFileSpace ( origRef, 2 ) ) break;	// Tolerate a file that ends abruptly.
		
		marker = XIO::ReadUns16_BE ( origRef );	// Read the next marker.
		if ( marker == 0xFFFF ) {
			// Have a pad byte, skip it. These are almost unheard of, so efficiency isn't critical.
			origRef->Seek ( -1, kXMP_SeekFromCurrent );	// Skip the first 0xFF, read the second again.
			continue;
		}

		if ( marker != 0xFFE0 ) break;	// Have a non-APP0 marker.
		XIO::WriteUns16_BE ( tempRef, marker );	// Write the APP0 marker.
		
		contentLen = XIO::ReadUns16_BE ( origRef );	// Copy the APP0 segment's length.
		XIO::WriteUns16_BE ( tempRef, contentLen );

		if ( contentLen < 2 ) XMP_Throw ( "Invalid JPEG segment length", kXMPErr_BadJPEG );
		contentLen -= 2;	// Reduce to just the content length.
		origRef->ReadAll ( buffer, contentLen );	// Copy the APP0 segment's content.
		tempRef->Write ( buffer, contentLen );

	}

	// Write the new Exif APP1 marker segment.

	XMP_Uns32 first4;

	if ( this->exifMgr != 0 ) {

		void* exifPtr;
		XMP_Uns32 exifLen = this->exifMgr->UpdateMemoryStream ( &exifPtr );
		if ( exifLen > kExifMaxDataLength ) exifLen = this->exifMgr->UpdateMemoryStream ( &exifPtr, true /* compact */ );

		while ( exifLen > 0 ) {
			XMP_Uns32 count = std::min ( exifLen, (XMP_Uns32) kExifMaxDataLength );
			first4 = MakeUns32BE ( 0xFFE10000 + 2 + kExifSignatureLength + count );
			tempRef->Write ( &first4, 4 );
			tempRef->Write ( kExifSignatureString, kExifSignatureLength );
			tempRef->Write ( exifPtr, count );
			exifPtr = (XMP_Uns8 *) exifPtr + count;
			exifLen -= count;
		}
	}

	// Write the new XMP APP1 marker segment, with possible extension marker segments.

	std::string mainXMP, extXMP, extDigest;
	SXMPUtils::PackageForJPEG ( this->xmpObj, &mainXMP, &extXMP, &extDigest );
	XMP_Assert ( (extXMP.size() == 0) || (extDigest.size() == 32) );

	first4 = MakeUns32BE ( 0xFFE10000 + 2 + kMainXMPSignatureLength + (XMP_Uns32)mainXMP.size() );
	tempRef->Write ( &first4, 4 );
	tempRef->Write ( kMainXMPSignatureString, kMainXMPSignatureLength );
	tempRef->Write ( mainXMP.c_str(), (XMP_Int32)mainXMP.size() );

	size_t extPos = 0;
	size_t extLen = extXMP.size();

	while ( extLen > 0 ) {

		size_t partLen = extLen;
		if ( partLen > 65000 ) partLen = 65000;

		first4 = MakeUns32BE ( 0xFFE10000 + 2 + kExtXMPPrefixLength + (XMP_Uns32)partLen );
		tempRef->Write ( &first4, 4 );

		tempRef->Write ( kExtXMPSignatureString, kExtXMPSignatureLength );
		tempRef->Write ( extDigest.c_str(), (XMP_Int32)extDigest.size() );

		first4 = MakeUns32BE ( (XMP_Int32)extXMP.size() );
		tempRef->Write ( &first4, 4 );
		first4 = MakeUns32BE ( (XMP_Int32)extPos );
		tempRef->Write ( &first4, 4 );

		tempRef->Write ( &extXMP[extPos], (XMP_Int32)partLen );

		extPos += partLen;
		extLen -= partLen;

	}

	// Write the new PSIR APP13 marker segments.
	if ( this->psirMgr != 0 ) {

		void* psirPtr;
		XMP_Uns32 psirLen = this->psirMgr->UpdateMemoryResources ( &psirPtr );
		while ( psirLen > 0 ) {
			XMP_Uns32 count = std::min ( psirLen, (XMP_Uns32) kPSIRMaxDataLength );
			first4 = MakeUns32BE ( 0xFFED0000 + 2 + kPSIRSignatureLength + count );
			tempRef->Write ( &first4, 4 );
			tempRef->Write ( kPSIRSignatureString, kPSIRSignatureLength );
			tempRef->Write ( psirPtr, count );
			psirPtr = (XMP_Uns8 *) psirPtr + count;
			psirLen -= count;
		}
	}

	// Copy remaining marker segments, skipping old metadata, to the first SOS marker or to EOI.
	origRef->Seek ( -2, kXMP_SeekFromCurrent );	// Back up to the marker from the end of the APP0 copy loop.
	
	while ( true ) {

		if ( checkAbort && abortProc(abortArg) ) {
			XMP_Throw ( "JPEG_MetaHandler::WriteFile - User abort", kXMPErr_UserAbort );
		}

		if ( ! XIO::CheckFileSpace ( origRef, 2 ) ) break;	// Tolerate a file that ends abruptly.
		
		marker = XIO::ReadUns16_BE ( origRef );	// Read the next marker.
		if ( marker == 0xFFFF ) {
			// Have a pad byte, skip it. These are almost unheard of, so efficiency isn't critical.
			origRef->Seek ( -1, kXMP_SeekFromCurrent );	// Skip the first 0xFF, read the second again.
			continue;
		}

		if ( (marker == 0xFFDA) || (marker == 0xFFD9) ) {	// Quit at the first SOS marker or at EOI.
			origRef->Seek ( -2, kXMP_SeekFromCurrent );	// The tail copy must include this marker.
			break;
		}

		if ( (marker == 0xFF01) ||	// Ill-formed file if we encounter a TEM or RSTn marker.
			 ((0xFFD0 <= marker) && (marker <= 0xFFD7)) ) {
			XMP_Throw ( "Unexpected TEM or RSTn marker", kXMPErr_BadJPEG );
		}

		contentLen = XIO::ReadUns16_BE ( origRef );	// Read this segment's length.
		if ( contentLen < 2 ) XMP_Throw ( "Invalid JPEG segment length", kXMPErr_BadJPEG );
		contentLen -= 2;	// Reduce to just the content length.
		
		XMP_Int64 contentOrigin = origRef->Offset();
		bool copySegment = true;
		size_t signatureLen;

		if ( (marker == 0xFFED) && (contentLen >= kPSIRSignatureLength) ) {

			// This is an APP13 segment, skip if it is the old PSIR.
			signatureLen = origRef->Read ( buffer, kPSIRSignatureLength );
			if ( (signatureLen == kPSIRSignatureLength) &&
				 CheckBytes ( &buffer[0], kPSIRSignatureString, kPSIRSignatureLength ) ) {
				copySegment = false;
			}

		} else if ( (marker == 0xFFE1) && (contentLen >= kExifSignatureLength) ) {	// Check for the shortest signature.

			// This is an APP1 segment, skip if it is the old Exif or XMP.
			
			XMP_Assert ( (kExifSignatureLength < kMainXMPSignatureLength) &&
						 (kMainXMPSignatureLength < kExtXMPSignatureLength) );
			signatureLen = origRef->Read ( buffer, kExtXMPSignatureLength );	// Read for the longest signature.

			if ( (signatureLen >= kExifSignatureLength) &&
				 (CheckBytes ( &buffer[0], kExifSignatureString, kExifSignatureLength ) ||
				  CheckBytes ( &buffer[0], kExifSignatureAltStr, kExifSignatureLength )) ) {
				copySegment = false;
			}
			
			if ( copySegment && (signatureLen >= kMainXMPSignatureLength) &&
				 CheckBytes ( &buffer[0], kMainXMPSignatureString, kMainXMPSignatureLength ) ) {
				copySegment = false;
			}
			
			if ( copySegment && (signatureLen == kExtXMPSignatureLength) &&
				 CheckBytes ( &buffer[0], kExtXMPSignatureString, kExtXMPPrefixLength ) ) {
				copySegment = false;
			}
			
		}
		
		if ( ! copySegment ) {
			origRef->Seek ( (contentOrigin + contentLen), kXMP_SeekFromStart );
		} else {
			XIO::WriteUns16_BE ( tempRef, marker );
			XIO::WriteUns16_BE ( tempRef, (contentLen + 2) );
			origRef->Seek ( contentOrigin, kXMP_SeekFromStart );
			origRef->ReadAll ( buffer, contentLen );
			tempRef->Write ( buffer, contentLen );
		}

	}

	// Copy the remainder of the source file.

	XIO::Copy ( origRef, tempRef, (origLength - origRef->Offset()) );
	this->needsUpdate = false;

}	// JPEG_MetaHandler::WriteTempFile