Exemplo n.º 1
0
void GIF_MetaHandler::WriteTempFile ( XMP_IO* tempRef )
{
	XMP_Assert( this->needsUpdate );

	XMP_IO* originalRef = this->parent->ioRef;
	originalRef->Rewind();

	tempRef->Truncate ( 0 );
	
	if ( XMPPacketOffset != 0 )
	{
		// Copying blocks before XMP Application Block
		XIO::Copy( originalRef, tempRef, XMPPacketOffset );

		// Writing XMP Packet
		tempRef->Write( this->xmpPacket.c_str(), (XMP_Uns32)this->xmpPacket.size() );

		// Copying Rest of the file
		originalRef->Seek( XMPPacketLength, kXMP_SeekFromCurrent );
		XIO::Copy( originalRef, tempRef, originalRef->Length() - originalRef->Offset() );

	}
	else
	{
		if ( trailerOffset == 0 )
			XMP_Throw( "Not able to write XMP packet in GIF file", kXMPErr_BadFileFormat );

		// Copying blocks before XMP Application Block
		XIO::Copy( originalRef, tempRef, trailerOffset );

		// Writing Extension Introducer 
		XIO::WriteUns8( tempRef, kXMP_block_Extension );

		// Writing Application Extension label
		XIO::WriteUns8( tempRef, 0xFF );

		// Writing Application Extension label
		XIO::WriteUns8( tempRef, APP_ID_LEN );

		// Writing Application Extension label
		tempRef->Write( XMP_APP_ID_DATA, APP_ID_LEN );

		// Writing XMP Packet
		tempRef->Write( this->xmpPacket.c_str(), (XMP_Uns32)this->xmpPacket.size() );

		// Writing Magic trailer
		XMP_Uns8 magicByte = 0x01;
		tempRef->Write( &magicByte, 1 );
		for ( magicByte = 0xFF; magicByte != 0x00; --magicByte )
			tempRef->Write( &magicByte, 1 );
		tempRef->Write( &magicByte, 1 );
		tempRef->Write( &magicByte, 1 );

		// Copying Rest of the file
		XIO::Copy( originalRef, tempRef, originalRef->Length() - originalRef->Offset() );

	}

}	// GIF_MetaHandler::WriteTempFile
void RIFF_MetaHandler::CacheFileData()
{
	this->containsXMP = false; //assume for now

	XMP_IO* file = this->parent->ioRef;
	this->oldFileSize = file ->Length();
	if ( (this->parent->format == kXMP_WAVFile) && (this->oldFileSize > 0xFFFFFFFF) )
		XMP_Throw ( "RIFF_MetaHandler::CacheFileData: WAV Files larger 4GB not supported", kXMPErr_Unimplemented );

	file ->Rewind();
	this->level = 0;

	// parse top-level chunks (most likely just one, except large avi files)
	XMP_Int64 filePos = 0;
	while ( filePos < this->oldFileSize )
	{

		this->riffChunks.push_back( (RIFF::ContainerChunk*) RIFF::getChunk( NULL, this ) );

		// Tolerate limited forms of trailing garbage in a file. Some apps append private data.

		filePos = file->Offset();
		XMP_Int64 fileTail = this->oldFileSize - filePos;

		if ( fileTail != 0 ) {

			if ( fileTail < 12 ) {

				this->oldFileSize = filePos;	// Pretend the file is smaller.
				this->trailingGarbageSize = fileTail;

			} else if ( this->parent->format == kXMP_WAVFile ) {

				if ( fileTail < 1024*1024 ) {
					this->oldFileSize = filePos;	// Pretend the file is smaller.
					this->trailingGarbageSize = fileTail;
				} else {
					XMP_Throw ( "Excessive garbage at end of file", kXMPErr_BadFileFormat )
				}

			} else {

				XMP_Int32 chunkInfo [3];
				file->ReadAll ( &chunkInfo, 12 );
				file->Seek ( -12, kXMP_SeekFromCurrent );
				if ( (GetUns32LE ( &chunkInfo[0] ) != RIFF::kChunk_RIFF) || (GetUns32LE ( &chunkInfo[2] ) != RIFF::kType_AVIX) ) {
					if ( fileTail < 1024*1024 ) {
						this->oldFileSize = filePos;	// Pretend the file is smaller.
						this->trailingGarbageSize = fileTail;
					} else {
						XMP_Throw ( "Excessive garbage at end of file", kXMPErr_BadFileFormat )
					}
				}

			}

		}

	}
Exemplo n.º 3
0
void WEBP_MetaHandler::CacheFileData()
{
    this->containsXMP = false; // assume for now

    XMP_IO* file = this->parent->ioRef;
    this->initialFileSize = file->Length();

    file->Rewind();

    XMP_Int64 filePos = 0;
    while (filePos < this->initialFileSize) {
        this->mainChunk = new WEBP::Container(this);
        filePos = file->Offset();
    }

    // covered before => internal error if it occurs
    XMP_Validate(file->Offset() == this->initialFileSize,
                 "WEBP_MetaHandler::CacheFileData: unknown data at end of file",
                 kXMPErr_InternalFailure);
}
Exemplo n.º 4
0
// parsing creation
Chunk::Chunk( ContainerChunk* parent_, RIFF_MetaHandler* handler, bool skip, ChunkType c )
{
	chunkType = c; // base class assumption
	this->parent = parent_;
	this->oldSize = 0;
	this->hasChange = false; // [2414649] valid assumption at creation time

	XMP_IO* file = handler->parent->ioRef;

	this->oldPos = file->Offset();
	this->id = XIO::ReadUns32_LE( file );
	this->oldSize = XIO::ReadUns32_LE( file );
	this->oldSize += 8;

	// Make sure the size is within expected bounds.
	XMP_Int64 chunkEnd = this->oldPos + this->oldSize;
	XMP_Int64 chunkLimit = handler->oldFileSize;
	if ( parent_ != 0 ) chunkLimit = parent_->oldPos + parent_->oldSize;
	if ( chunkEnd > chunkLimit ) {
		bool isUpdate = XMP_OptionIsSet ( handler->parent->openFlags, kXMPFiles_OpenForUpdate );
		bool repairFile = XMP_OptionIsSet ( handler->parent->openFlags, kXMPFiles_OpenRepairFile );
		if ( (! isUpdate) || (repairFile && (parent_ == 0)) ) {
			this->oldSize = chunkLimit - this->oldPos;
		} else {
			XMP_Throw ( "Bad RIFF chunk size", kXMPErr_BadFileFormat );
		}
	}

	this->newSize = this->oldSize;
	this->needSizeFix = false;

	if ( skip ) file->Seek ( (this->oldSize - 8), kXMP_SeekFromCurrent );

	// "good parenting", essential for latter destruction.
	if ( this->parent != NULL )
	{
		this->parent->children.push_back( this );
		if( this->chunkType == chunk_VALUE )
			this->parent->childmap.insert( std::make_pair( this->id, (ValueChunk*) this ) );
	}
}
Exemplo n.º 5
0
// b) parsing
ContainerChunk::ContainerChunk( ContainerChunk* parent, RIFF_MetaHandler* handler ) : Chunk( parent, handler, false, chunk_CONTAINER )
{
	bool repairMode = ( 0 != ( handler->parent->openFlags & kXMPFiles_OpenRepairFile ));

	try
	{
		XMP_IO* file = handler->parent->ioRef;
		XMP_Uns8 level = handler->level;

		// get type of container chunk
		this->containerType = XIO::ReadUns32_LE( file );

		// ensure legality of top-level chunks
		if ( level == 0 && handler->riffChunks.size() > 0 )
		{
			XMP_Validate( handler->parent->format == kXMP_AVIFile, "only AVI may have multiple top-level chunks", kXMPErr_BadFileFormat );
			XMP_Validate( this->containerType == kType_AVIX, "all chunks beyond main chunk must be type AVIX", kXMPErr_BadFileFormat );
		}

		// has *relevant* subChunks? (there might be e.g. non-INFO LIST chunks we don't care about)
		bool hasSubChunks = ( ( this->id == kChunk_RIFF ) ||
							  ( this->id == kChunk_LIST && this->containerType == kType_INFO ) ||
							  ( this->id == kChunk_LIST && this->containerType == kType_Tdat ) ||
							  ( this->id == kChunk_LIST && this->containerType == kType_hdrl )
						  );
		XMP_Int64 endOfChunk = this->oldPos + this->oldSize;

		// this statement catches beyond-EoF-offsets on any level
		// exception: level 0, tolerate if in repairMode
		if ( (level == 0) && repairMode && (endOfChunk > handler->oldFileSize) )
		{
			endOfChunk = handler->oldFileSize; // assign actual file size
			this->oldSize = endOfChunk - this->oldPos; //reversely calculate correct oldSize
		}

		XMP_Validate( endOfChunk <= handler->oldFileSize, "offset beyond EoF", kXMPErr_BadFileFormat );

		Chunk* curChild = 0;
		if ( hasSubChunks )
		{
			handler->level++;
			while ( file->Offset() < endOfChunk )
			{
				curChild = RIFF::getChunk( this, handler );

				// digest pad byte - no value validation (0), since some 3rd party files have non-0-padding.
				if ( file->Offset() % 2 == 1 )
				{
					// [1521093] tolerate missing pad byte at very end of file:
					XMP_Uns8 pad;
					file->Read ( &pad, 1 );  // Read the pad, tolerate being at EOF.

				}

				// within relevant LISTs, relentlesly delete junk chunks (create a single one
				// at end as part of updateAndChanges()
				if ( (containerType== kType_INFO || containerType == kType_Tdat)
						&& ( curChild->chunkType == chunk_JUNK ) )
				{
						this->children.pop_back();
						delete curChild;
				} // for other chunks: join neighouring Junk chunks into one
				else if ( (curChild->chunkType == chunk_JUNK) && ( this->children.size() >= 2 ) )
				{
					// nb: if there are e.g 2 chunks, then last one is at(1), prev one at(0) ==> '-2'
					Chunk* prevChunk = this->children.at( this->children.size() - 2 );
					if ( prevChunk->chunkType == chunk_JUNK )
					{
						// stack up size to prior chunk
						prevChunk->oldSize += curChild->oldSize;
						prevChunk->newSize += curChild->newSize;
						XMP_Enforce( prevChunk->oldSize == prevChunk->newSize );
						// destroy current chunk
						this->children.pop_back();
						delete curChild;
					}
				}
			}
			handler->level--;
			XMP_Validate( file->Offset() == endOfChunk, "subchunks exceed outer chunk size", kXMPErr_BadFileFormat );

			// pointers for later legacy processing
			if ( level==1 && this->id==kChunk_LIST && this->containerType == kType_INFO )
				handler->listInfoChunk = this;
			if ( level==1 && this->id==kChunk_LIST && this->containerType == kType_Tdat )
				handler->listTdatChunk = this;
			if ( level == 1 && this->id == kChunk_LIST && this->containerType == kType_hdrl )
				handler->listHdlrChunk = this;
		}
		else // skip non-interest container chunk
		{
			file->Seek ( (this->oldSize - 8 - 4), kXMP_SeekFromCurrent );
		} // if - else

	} // try
	catch (XMP_Error& e) {
		this->release(); // free resources
		if ( this->parent != 0)
			this->parent->children.pop_back(); // hereby taken care of, so removing myself...

		throw e;         // re-throw
	}
}
Exemplo n.º 6
0
// =================================================================================================
// SVG_MetaHandler::WriteTempFile
// ==============================
//
void SVG_MetaHandler::WriteTempFile( XMP_IO* tempRef )
{
	XMP_Assert( this->needsUpdate );

	XMP_IO* sourceRef = this->parent->ioRef;
	if ( sourceRef == NULL || svgNode == NULL )
		return;

	tempRef->Rewind();
	sourceRef->Rewind();

	XMP_Int64 currentOffset = svgAdapter->firstSVGElementOffset;
	XIO::Copy( sourceRef, tempRef, currentOffset );

	OffsetStruct titleOffset = svgAdapter->GetElementOffsets( "title" );
	OffsetStruct descOffset = svgAdapter->GetElementOffsets( "desc" );
	OffsetStruct metadataOffset = svgAdapter->GetElementOffsets( "metadata" );

	std::string title;
	std::string description;

	XML_NodePtr titleNode = svgNode->GetNamedElement( svgNode->ns.c_str(), "title" );
	( void ) this->xmpObj.GetLocalizedText( kXMP_NS_DC, "title", "", "x-default", 0, &title, 0 );

	XML_NodePtr descNode = svgNode->GetNamedElement( svgNode->ns.c_str(), "desc" );
	( void ) this->xmpObj.GetLocalizedText( kXMP_NS_DC, "description", "", "x-default", 0, &description, 0 );

	// Need to cover the case of both workflows
	// This would have been called after inplace is not possible
	// This would have called for safe update
	if ( !isTitleUpdateReq )
	{
		if ( ( titleNode == NULL ) == ( title.empty() ) )
		{
			if ( titleNode != NULL && titleNode->content.size() == 1 && titleNode->content[ 0 ]->kind == kCDataNode && !XMP_LitMatch( titleNode->content[ 0 ]->value.c_str(), title.c_str() ) )
				isTitleUpdateReq = true;
		}
		else
			isTitleUpdateReq = true;
	}
	if ( !isDescUpdateReq )
	{
		if ( ( descNode == NULL ) == ( description.empty() ) )
		{
			if ( descNode != NULL && descNode->content.size() == 1 && descNode->content[ 0 ]->kind == kCDataNode &&  !XMP_LitMatch( descNode->content[ 0 ]->value.c_str(), description.c_str() ) )
				isDescUpdateReq = true;
		}
		else
			isDescUpdateReq = true;
	}

	// Initial Insertion/Updation

	// Insert/Update Title if requires
	// Don't insert/update it if Metadata or desc child comes before title child
	bool isTitleWritten = !isTitleUpdateReq;
	if ( isTitleUpdateReq )
	{
		// Insertion Case
		if ( titleNode == NULL )
		{
			InsertNewTitle( tempRef, title );
			isTitleWritten = true;
		}
		else if ( ( descOffset.startOffset == -1 || titleOffset.startOffset < descOffset.startOffset )	// Updation/Deletion Case
			&& ( metadataOffset.startOffset == -1 || titleOffset.startOffset < metadataOffset.startOffset ) )
		{
			ProcessTitle( sourceRef, tempRef, title, currentOffset, titleOffset );
			isTitleWritten = true;
		}
	}

	// Insert/Update Description if requires
	// Don't insert/update it if Metadata child comes before desc child
	bool isDescWritten = !isDescUpdateReq;
	if ( isDescUpdateReq )
	{
		if ( descNode == NULL )
		{
			if ( titleOffset.nextOffset != -1 )
			{
				XIO::Copy( sourceRef, tempRef, titleOffset.nextOffset - currentOffset );
				currentOffset = titleOffset.nextOffset;
			}
			InsertNewDescription( tempRef, description );
			isDescWritten = true;
		}
		else if ( metadataOffset.startOffset == -1 || descOffset.startOffset < metadataOffset.startOffset )
		{
			ProcessDescription( sourceRef, tempRef, description, currentOffset, descOffset );
			isDescWritten = true;
		}
	}

	// Insert/Update Metadata if requires
	// Don't insert/update it if case is DTM
	bool isMetadataWritten = false;
	if ( metadataOffset.startOffset == -1 )
	{
		if ( descOffset.nextOffset != -1 )
		{
			XIO::Copy( sourceRef, tempRef, descOffset.nextOffset - currentOffset );
			currentOffset = descOffset.nextOffset;
		}
		else if ( titleOffset.nextOffset != -1 )
		{
			XIO::Copy( sourceRef, tempRef, titleOffset.nextOffset - currentOffset );
			currentOffset = titleOffset.nextOffset;
		}
		InsertNewMetadata( tempRef, this->xmpPacket );
		isMetadataWritten = true;
	}
	else if ( !( !isTitleWritten && isDescWritten && titleOffset.startOffset < metadataOffset.startOffset ) )		// Not DTM
	{
		// No XMP packet was present in the file
		if ( this->packetInfo.offset == kXMPFiles_UnknownOffset )
		{
			std::string metadataElement = "<metadata>";
			XIO::Copy( sourceRef, tempRef, metadataOffset.startOffset - currentOffset + metadataElement.length() );
			currentOffset = sourceRef->Offset();
			tempRef->Write( this->xmpPacket.c_str(), static_cast< int >( this->xmpPacket.length() ) );
		}
		else	// Replace XMP Packet
		{
			XIO::Copy( sourceRef, tempRef, this->packetInfo.offset - currentOffset );
			tempRef->Write( this->xmpPacket.c_str(), static_cast< int >( this->xmpPacket.length() ) );
			sourceRef->Seek( this->packetInfo.offset + this->packetInfo.length, kXMP_SeekFromStart );
			currentOffset = sourceRef->Offset();
		}
		isMetadataWritten = true;
	}

	// If simple cases was followed then copy rest file
	if ( isTitleWritten && isDescWritten && isMetadataWritten )
	{
		XIO::Copy( sourceRef, tempRef, ( sourceRef->Length() - currentOffset ) );
		return;
	}

	// If the case is not Simple (TDM) then perform these operations
	if ( isDescWritten )		// TDM, DTM, DMT
	{
		if ( !isTitleWritten )		// DTM, DMT
		{
			if ( titleOffset.startOffset < metadataOffset.startOffset )		// DTM
			{
				ProcessTitle( sourceRef, tempRef, title, currentOffset, titleOffset );
				isTitleWritten = true;

				if ( this->packetInfo.offset == kXMPFiles_UnknownOffset )
				{
					std::string metadataElement = "<metadata>";
					XIO::Copy( sourceRef, tempRef, metadataOffset.startOffset - currentOffset + metadataElement.length() );
					currentOffset = sourceRef->Offset();
					tempRef->Write( this->xmpPacket.c_str(), static_cast< int >( this->xmpPacket.length() ) );
				}
				else
				{
					XIO::Copy( sourceRef, tempRef, this->packetInfo.offset - currentOffset );
					tempRef->Write( this->xmpPacket.c_str(), static_cast< int >( this->xmpPacket.length() ) );
					sourceRef->Seek( this->packetInfo.offset + this->packetInfo.length, kXMP_SeekFromStart );
					currentOffset = sourceRef->Offset();
				}
				isMetadataWritten = true;

			}
			else	// DMT
			{
				ProcessTitle( sourceRef, tempRef, title, currentOffset, titleOffset );
				isTitleWritten = true;
			}
		}
		// Else
		// Would have already covered this case: TDM

	}
	else		//  TMD, MDT, MTD
	{
		if ( isTitleWritten )		// TMD
		{
			ProcessDescription( sourceRef, tempRef, description, currentOffset, descOffset );
			isDescWritten = true;
		}
		else		// MDT or MTD
		{
			if ( titleOffset.startOffset < descOffset.startOffset )	// MTD
			{
				ProcessTitle( sourceRef, tempRef, title, currentOffset, titleOffset );
				isTitleWritten = true;

				ProcessDescription( sourceRef, tempRef, description, currentOffset, descOffset );
				isDescWritten = true;
			}
			else		// MDT
			{
				ProcessDescription( sourceRef, tempRef, description, currentOffset, descOffset );
				isDescWritten = true;

				ProcessTitle( sourceRef, tempRef, title, currentOffset, titleOffset );
				isTitleWritten = true;
			}
		}
	}

	// Finally Everything would have been written
	XMP_Enforce( isTitleWritten && isDescWritten && isMetadataWritten );
	XIO::Copy( sourceRef, tempRef, ( sourceRef->Length() - currentOffset ) );
	this->needsUpdate = false;

}	// SVG_MetaHandler::WriteTempFile
Exemplo n.º 7
0
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
Exemplo n.º 8
0
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
void MP3_MetaHandler::CacheFileData()
{

    //*** abort procedures
    this->containsXMP = false;		//assume no XMP for now

    XMP_IO* file = this->parent->ioRef;
    XMP_PacketInfo &packetInfo = this->packetInfo;

    file->Rewind();

    this->hasID3Tag = this->id3Header.read( file );
    this->majorVersion = this->id3Header.fields[ID3Header::o_vMajor];
    this->minorVersion = this->id3Header.fields[ID3Header::o_vMinor];
    this->hasExtHeader = (0 != ( 0x40 & this->id3Header.fields[ID3Header::o_flags])); //'naturally' false if no ID3Tag
    this->hasFooter = ( 0 != ( 0x10 & this->id3Header.fields[ID3Header::o_flags])); //'naturally' false if no ID3Tag

    // stored size is w/o initial header (thus adding 10)
    // + but extended header (if existing)
    // + padding + frames after unsynchronisation (?)
    // (if no ID3 tag existing, constructed default correctly sets size to 10.)
    this->oldTagSize = ID3Header::kID3_TagHeaderSize + synchToInt32(GetUns32BE( &id3Header.fields[ID3Header::o_size] ));

    if ( ! hasExtHeader ) {

        this->extHeaderSize = 0; // := there is no such header.

    } else {

        this->extHeaderSize = synchToInt32( XIO::ReadInt32_BE( file));
        XMP_Uns8 extHeaderNumFlagBytes = XIO::ReadUns8( file );

        // v2.3 doesn't include the size, while v2.4 does
        if ( this->majorVersion < 4 ) this->extHeaderSize += 4;
        XMP_Validate( this->extHeaderSize >= 6, "extHeader size too small", kXMPErr_BadFileFormat );

        file->Seek ( this->extHeaderSize - 6, kXMP_SeekFromCurrent );

    }

    this->framesVector.clear(); //mac precaution
    ID3v2Frame* curFrame = 0; // reusable

    ////////////////////////////////////////////////////
    // read frames

    XMP_Uns32 xmpID = XMP_V23_ID;
    if ( this->majorVersion == 2 ) xmpID = XMP_V22_ID;

    while ( file->Offset() < this->oldTagSize ) {

        curFrame = new ID3v2Frame();

        try {
            XMP_Int64 frameSize = curFrame->read ( file, this->majorVersion );
            if ( frameSize == 0 ) {
                delete curFrame; // ..since not becoming part of vector for latter delete.
                break;			 // not a throw. There's nothing wrong with padding.
            }
            this->containsXMP = true;
        } catch ( ... ) {
            delete curFrame;
            throw;
        }

        // these are both pointer assignments, no (copy) construction
        // (MemLeak Note: for all things pushed, memory cleanup is taken care of in destructor.)
        this->framesVector.push_back ( curFrame );

        //remember XMP-Frame, if it occurs:
        if ( (curFrame->id ==xmpID) &&
                (curFrame->contentSize > 8) && CheckBytes ( &curFrame->content[0], "XMP\0", 4 ) ) {

            // be sure that this is the first packet (all else would be illegal format)
            XMP_Validate ( this->framesMap[xmpID] == 0, "two XMP packets in one file", kXMPErr_BadFileFormat );
            //add this to map, needed on reconciliation
            this->framesMap[xmpID] = curFrame;

            this->packetInfo.length = curFrame->contentSize - 4; // content minus "XMP\0"
            this->packetInfo.offset = ( file->Offset() - this->packetInfo.length );

            this->xmpPacket.erase(); //safety
            this->xmpPacket.assign( &curFrame->content[4], curFrame->contentSize - 4 );
            this->containsXMP = true; // do this last, after all possible failure

        }

        // No space for another frame? => assume into ID3v2.4 padding.
        XMP_Int64 newPos = file->Offset();
        XMP_Int64 spaceLeft = this->oldTagSize - newPos;	// Depends on first check below!
        if ( (newPos > this->oldTagSize) || (spaceLeft < (XMP_Int64)ID3Header::kID3_TagHeaderSize) ) break;

    }

    ////////////////////////////////////////////////////
    // padding

    this->oldPadding = this->oldTagSize - file->Offset();
    this->oldFramesSize = this->oldTagSize - ID3Header::kID3_TagHeaderSize - this->oldPadding;

    XMP_Validate ( (this->oldPadding >= 0), "illegal oldTagSize or padding value", kXMPErr_BadFileFormat );

    for ( XMP_Int64 i = this->oldPadding; i > 0; ) {
        if ( i >= 8 ) {
            if ( XIO::ReadInt64_BE(file) != 0 ) XMP_Throw ( "padding not nulled out", kXMPErr_BadFileFormat );
            i -= 8;
            continue;
        }
        if ( XIO::ReadUns8(file) != 0) XMP_Throw ( "padding(2) not nulled out", kXMPErr_BadFileFormat );
        i--;
    }

    //// read ID3v1 tag
    if ( ! this->containsXMP ) this->containsXMP = id3v1Tag.read ( file, &this->xmpObj );

}	// MP3_MetaHandler::CacheFileData