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.º 2
0
// b) parse
XMPChunk::XMPChunk( ContainerChunk* parent_, RIFF_MetaHandler* handler ) : Chunk( parent_, handler, false, chunk_XMP )
{
	chunkType = chunk_XMP;
	XMP_IO* file = handler->parent->ioRef;
	/*XMP_Uns8 level = handler->level*/;

	handler->packetInfo.offset = this->oldPos + 8;
	handler->packetInfo.length = (XMP_Int32) this->oldSize - 8;

	handler->xmpPacket.reserve ( handler->packetInfo.length );
	handler->xmpPacket.assign ( handler->packetInfo.length, ' ' );
	file->ReadAll ( (void*)handler->xmpPacket.data(), handler->packetInfo.length );

	handler->containsXMP = true; // last, after all possible failure

	// pointer for later processing
	handler->xmpChunk = this;
}
Exemplo n.º 3
0
// b) parsing
ValueChunk::ValueChunk( ContainerChunk* parent_, RIFF_MetaHandler* handler ) : Chunk( parent_, handler, false, chunk_VALUE )
{
	// set value: -----------------
	XMP_IO* file = handler->parent->ioRef;
	/*XMP_Uns8 level = handler->level;*/

	// unless changed through reconciliation, assume for now.
	// IMPORTANT to stay true to the original (no \0 cleanup or similar)
	// since unknown value chunks might not be fully understood,
	// hence must be precisely preserved !!!

	XMP_Int32 length = (XMP_Int32) this->oldSize - 8;
	this->oldValue.reserve( length );
	this->oldValue.assign( length + 1, '\0' );
	file->ReadAll ( (void*)this->oldValue.data(), length );

	this->newValue = this->oldValue;
	this->newSize = this->oldSize;
}
Exemplo n.º 4
0
void GIF_MetaHandler::CacheFileData()
{
	this->containsXMP = false;

	XMP_IO * fileRef = this->parent->ioRef;

	// Try to navigate through the blocks to find the XMP block.
	if ( this->ParseGIFBlocks( fileRef ) )
	{
		// XMP packet present
		this->xmpPacket.assign( XMPPacketLength, ' ' );

		// 13 bytes for the block size and 2 bytes for Extension ID and Label
		this->SeekFile( fileRef, XMPPacketOffset, kXMP_SeekFromStart );
		fileRef->ReadAll( ( void* )this->xmpPacket.data(), XMPPacketLength );

		this->packetInfo.offset = XMPPacketOffset;
		this->packetInfo.length = XMPPacketLength;
		this->containsXMP = true;
	}
	// else no XMP

}	// GIF_MetaHandler::CacheFileData
Exemplo n.º 5
0
void SVG_MetaHandler::CacheFileData() 
{
	XMP_Assert( !this->containsXMP );

	XMP_IO * fileRef = this->parent->ioRef;
	
	XMP_Uns8 marker[ 4 ];
	fileRef->Rewind();
	fileRef->Read( marker, 4 );

	// Checking for UTF-16 BOM and UTF-32 BOM
	if ( ( marker[ 0 ] == 0xFF && marker[ 1 ] == 0xFE ) || ( marker[ 0 ] == 0xFE && marker[ 1 ] == 0xFF ) || ( marker[ 0 ] == marker[ 1 ] == 0x00 && marker[ 2 ] == 0xFE && marker[ 3 ] == 0xFF ) )
	{
		XMP_Error error( kXMPErr_BadXML, "Invalid SVG file" );
		this->NotifyClient( &this->parent->errorCallback, kXMPErrSev_OperationFatal, error );
	}
		
	// Creating a new SVG Parser
	svgAdapter = new SVG_Adapter();
	if ( svgAdapter == 0 )
		XMP_Throw( "SVG_MetaHandler: Can't create SVG adapter", kXMPErr_NoMemory );
	svgAdapter->SetErrorCallback( &this->parent->errorCallback );

	// Registering all the required tags to SVG Parser
	svgAdapter->RegisterPI( "xpacket" );
	svgAdapter->RegisterElement( "metadata", "svg" );
	svgAdapter->RegisterElement( "xmpmeta", "metadata" );
	svgAdapter->RegisterElement( "RDF", "metadata" );
	svgAdapter->RegisterElement( "title", "svg" );
	svgAdapter->RegisterElement( "desc", "svg" );

	// Parsing the whole buffer
	fileRef->Rewind();
	XMP_Uns8 buffer[ 64 * 1024 ];
	while ( true ) {
		XMP_Int32 ioCount = fileRef->Read( buffer, sizeof( buffer ) );
		if ( ioCount == 0 || !svgAdapter->IsParsingRequire() ) break;
		svgAdapter->ParseBuffer( buffer, ioCount, false /* not the end */ );
	}
	svgAdapter->ParseBuffer( 0, 0, true );	// End the parse.

	XML_Node & xmlTree = this->svgAdapter->tree;
	XML_NodePtr rootElem = 0;

	for ( size_t i = 0, limit = xmlTree.content.size(); i < limit; ++i )
	{
		if ( xmlTree.content[ i ]->kind == kElemNode ) {
			rootElem = xmlTree.content[ i ];
		}
	}
	if ( rootElem == 0 )
		XMP_Throw( "Not a valid SVG File", kXMPErr_BadFileFormat );

	XMP_StringPtr rootLocalName = rootElem->name.c_str() + rootElem->nsPrefixLen;

	if ( ! XMP_LitMatch( rootLocalName, "svg" ) )
		XMP_Throw( "Not able to parse such SVG File", kXMPErr_BadFileFormat );
	
	// Making SVG node as Root Node
	svgNode = rootElem;

	bool FoundPI = false;
	bool FoundWrapper = false;
	XML_NodePtr metadataNode = svgNode->GetNamedElement( rootElem->ns.c_str(), "metadata" );

	// We are intersted only in the Metadata tag of outer SVG element
	// XMP should be present only in metadata Node of SVG
	if ( metadataNode != NULL )
	{
		XMP_Int64 packetLength = -1;
		XMP_Int64 packetOffset = -1;
		XMP_Int64 PIOffset = svgAdapter->GetPIOffset( "xpacket", 1 );
		OffsetStruct wrapperOffset = svgAdapter->GetElementOffsets( "xmpmeta" );
		OffsetStruct rdfOffset = svgAdapter->GetElementOffsets( "RDF" );
		
		// Checking XMP PI's position
		if ( PIOffset != -1 )
		{
			if ( wrapperOffset.startOffset != -1 && wrapperOffset.startOffset < PIOffset )
				packetOffset = wrapperOffset.startOffset;
			else
			{
				XMP_Int64 trailerOffset = svgAdapter->GetPIOffset( "xpacket", 2 );
				XML_NodePtr trailerNode = metadataNode->GetNamedElement( "", "xpacket", 1 );
				if ( trailerOffset != -1 || trailerNode != 0 )
				{
					packetLength = 2;								// "<?" = 2
					packetLength += trailerNode->name.length();		// Node's name
					packetLength += 1;								// Empty Space after Node's name
					packetLength += trailerNode->value.length();	// Value
					packetLength += 2;								// "?>" = 2
					packetLength += ( trailerOffset - PIOffset );
					packetOffset = PIOffset;
				}
			}
		}
		else if ( wrapperOffset.startOffset != -1 )		// XMP Wrapper is present without PI
		{
			XML_NodePtr wrapperNode = metadataNode->GetNamedElement( "adobe:ns:meta/", "xmpmeta" );
			if ( wrapperNode != 0 )
			{
				std::string trailerWrapper = "</x:xmpmeta>";
				packetLength = trailerWrapper.length();
				packetLength += ( wrapperOffset.endOffset - wrapperOffset.startOffset );
				packetOffset = wrapperOffset.startOffset;
			}
		}
		else		// RDF packet is present without PI and wrapper
		{
			XML_NodePtr rdfNode = metadataNode->GetNamedElement( "http://www.w3.org/1999/02/22-rdf-syntax-ns#", "RDF" );
			if ( rdfNode != 0 )
			{
				std::string rdfTrailer = "</rdf:RDF>";
				packetLength = rdfTrailer.length();
				packetLength += ( rdfOffset.endOffset - rdfOffset.startOffset );
				packetOffset = rdfOffset.startOffset;
			}
		}

		// Fill the necesarry information and packet with XMP data
		if ( packetOffset != -1 )
		{
			this->packetInfo.offset = packetOffset;
			this->packetInfo.length = ( XMP_Int32 ) packetLength;
			this->xmpPacket.assign( this->packetInfo.length, ' ' );
			fileRef->Seek( packetOffset, kXMP_SeekFromStart );
			fileRef->ReadAll( ( void* )this->xmpPacket.data(), this->packetInfo.length );
			FillPacketInfo( this->xmpPacket, &this->packetInfo );
			this->containsXMP = true;
			return;
		}
	}
	this->containsXMP = false;	

}	// SVG_MetaHandler::CacheFileData
void Scanner_MetaHandler::CacheFileData()
{
	XMP_IO* fileRef   = this->parent->ioRef;
	bool        beLenient = XMP_OptionIsClear ( this->parent->openFlags, kXMPFiles_OpenStrictly );

	int			pkt;
	XMP_Int64	bufPos;
	size_t		bufLen;
	SXMPMeta *	newMeta;

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

	std::vector<CandidateInfo> candidates;	// ! These have SXMPMeta* fields, don't leak on exceptions.

	this->containsXMP = false;

	try {

		// ------------------------------------------------------
		// Scan the entire file to find all of the valid packets.

		XMP_Int64  fileLen = fileRef->Length();
		XMPScanner scanner ( fileLen );

		enum { kBufferSize = 64*1024 };
		XMP_Uns8	buffer [kBufferSize];

		fileRef->Rewind();

		for ( bufPos = 0; bufPos < fileLen; bufPos += bufLen ) {
			if ( checkAbort && abortProc(abortArg) ) {
				XMP_Throw ( "Scanner_MetaHandler::LocateXMP - User abort", kXMPErr_UserAbort );
			}
			bufLen = fileRef->Read ( buffer, kBufferSize );
			if ( bufLen == 0 ) XMP_Throw ( "Scanner_MetaHandler::LocateXMP: Read failure", kXMPErr_ExternalFailure );
			scanner.Scan ( buffer, bufPos, bufLen );
		}

		// --------------------------------------------------------------
		// Parse the valid packet snips, building a vector of candidates.

		long snipCount = scanner.GetSnipCount();

		XMPScanner::SnipInfoVector snips ( snipCount );
		scanner.Report ( snips );

		for ( pkt = 0; pkt < snipCount; ++pkt ) {

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

			// Seek to the packet then try to parse it.

			if ( snips[pkt].fState != XMPScanner::eValidPacketSnip ) continue;
			fileRef->Seek ( snips[pkt].fOffset, kXMP_SeekFromStart );
			newMeta = new SXMPMeta();
			std::string xmpPacket;
			xmpPacket.reserve ( (size_t)snips[pkt].fLength );

			try {
				for ( bufPos = 0; bufPos < snips[pkt].fLength; bufPos += bufLen ) {
					bufLen = kBufferSize;
					if ( (bufPos + bufLen) > (size_t)snips[pkt].fLength ) bufLen = size_t ( snips[pkt].fLength - bufPos );
					(void) fileRef->ReadAll ( buffer, (XMP_Int32)bufLen );
					xmpPacket.append ( (const char *)buffer, bufLen );
					newMeta->ParseFromBuffer ( (char *)buffer, (XMP_StringLen)bufLen, kXMP_ParseMoreBuffers );
				}
				newMeta->ParseFromBuffer ( 0, 0, kXMP_NoOptions );
			} catch ( ... ) {
				delete newMeta;
				if ( beLenient ) continue;	// Skip if we're being lenient, else rethrow.
				throw;
			}

			// It parsed OK, add it to the array of candidates.

			candidates.push_back ( CandidateInfo() );
			CandidateInfo & newInfo = candidates.back();
			newInfo.xmpObj = newMeta;
			newInfo.xmpPacket.swap ( xmpPacket );
			newInfo.packetInfo.offset = snips[pkt].fOffset;
			newInfo.packetInfo.length = (XMP_Int32)snips[pkt].fLength;
			newInfo.packetInfo.charForm  = snips[pkt].fCharForm;
			newInfo.packetInfo.writeable = (snips[pkt].fAccess == 'w');

		}

		// ----------------------------------------
		// Figure out which packet is the main one.

		int main = PickMainPacket ( candidates, beLenient );

		if ( main != -1 ) {
			this->packetInfo = candidates[main].packetInfo;
			this->xmpPacket.swap ( candidates[main].xmpPacket );
			this->xmpObj = *candidates[main].xmpObj;
			this->containsXMP = true;
			this->processedXMP = true;
		}

		for ( pkt = 0; pkt < (int)candidates.size(); ++pkt ) {
			if ( candidates[pkt].xmpObj != 0 ) delete candidates[pkt].xmpObj;
		}

	} catch ( ... ) {

		// Clean up the SXMPMeta* fields from the vector of candidates.
		for ( pkt = 0; pkt < (int)candidates.size(); ++pkt ) {
			if ( candidates[pkt].xmpObj != 0 ) delete candidates[pkt].xmpObj;
		}
		throw;

	}

}	// Scanner_MetaHandler::CacheFileData
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
Exemplo n.º 9
0
void InDesign_MetaHandler::CacheFileData()
{
	XMP_IO* fileRef = this->parent->ioRef;
	XMP_PacketInfo & packetInfo = this->packetInfo;

	XMP_Assert ( kINDD_PageSize == sizeof(InDesignMasterPage) );
	static const size_t kBufferSize = (2 * kINDD_PageSize);
	XMP_Uns8 buffer [kBufferSize];

	size_t	 dbPages;
	XMP_Uns8 cobjEndian;

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

	this->containsXMP = false;

	// ---------------------------------------------------------------------------------
	// Figure out which master page is active and seek to the contiguous object portion.

	{
		fileRef->Rewind();
		fileRef->ReadAll ( buffer, (2 * kINDD_PageSize) );

		InDesignMasterPage * masters = (InDesignMasterPage *) &buffer[0];
		XMP_Uns64 seq0 = GetUns64LE ( (XMP_Uns8 *) &masters[0].fSequenceNumber );
		XMP_Uns64 seq1 = GetUns64LE ( (XMP_Uns8 *) &masters[1].fSequenceNumber );

		dbPages = GetUns32LE ( (XMP_Uns8 *) &masters[0].fFilePages );
		cobjEndian = masters[0].fObjectStreamEndian;
		if ( seq1 > seq0 ) {
			dbPages = GetUns32LE ( (XMP_Uns8 *)  &masters[1].fFilePages );
			cobjEndian = masters[1].fObjectStreamEndian;
		}
	}

	XMP_Assert ( ! this->streamBigEndian );
	if ( cobjEndian == kINDD_BigEndian ) this->streamBigEndian = true;

	// ---------------------------------------------------------------------------------------------
	// Look for the XMP contiguous object. Each contiguous object has a header and trailer, both of
	// the InDesignContigObjMarker structure. The stream size in the header/trailer is the number of
	// data bytes between the header and trailer. The XMP stream begins with a 4 byte size of the
	// XMP packet. Yes, this is the contiguous object data size minus 4 - silly but true. The XMP
	// must have a packet wrapper, the leading xpacket PI is used as the marker of XMP.

	XMP_Int64 cobjPos = (XMP_Int64)dbPages * kINDD_PageSize;	// ! Use a 64 bit multiply!
	cobjPos -= (2 * sizeof(InDesignContigObjMarker));			// ! For the first pass in the loop.
	XMP_Uns32 streamLength = 0;									// ! For the first pass in the loop.

	while ( true ) {

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

		// Fetch the start of the next stream and check the contiguous object header.
		// ! The writeable bit of fObjectClassID is ignored, we use the packet trailer flag.

		cobjPos += streamLength + (2 * sizeof(InDesignContigObjMarker));
		fileRef->Seek ( cobjPos, kXMP_SeekFromStart );
		fileRef->ReadAll ( buffer, sizeof(InDesignContigObjMarker) );

		const InDesignContigObjMarker * cobjHeader = (const InDesignContigObjMarker *) &buffer[0];
		if ( ! CheckBytes ( Uns8Ptr(&cobjHeader->fGUID), kINDDContigObjHeaderGUID, kInDesignGUIDSize ) ) break;	// Not a contiguous object header.
		this->xmpObjID = cobjHeader->fObjectUID;	// Save these now while the buffer is good.
		this->xmpClassID = cobjHeader->fObjectClassID;
		streamLength = GetUns32LE ( (XMP_Uns8 *) &cobjHeader->fStreamLength );

		// See if this is the XMP stream.

		if ( streamLength < (4 + kUTF8_PacketHeaderLen + kUTF8_PacketTrailerLen) ) continue;	// Too small, can't possibly be XMP.

		fileRef->ReadAll ( buffer, (4 + kUTF8_PacketHeaderLen) );
		XMP_Uns32 innerLength = GetUns32LE ( &buffer[0] );
		if ( this->streamBigEndian ) innerLength = GetUns32BE ( &buffer[0] );
		if ( innerLength != (streamLength - 4) ) {
			// Be tolerant of a mistake with the endian flag.
			innerLength = Flip4 ( innerLength );
			if ( innerLength != (streamLength - 4) ) continue;	// Not legit XMP.
		}

		XMP_Uns8 * chPtr = &buffer[4];
		size_t startLen = strlen((char*)kUTF8_PacketStart);
		size_t idLen = strlen((char*)kUTF8_PacketID);
		
		if ( ! CheckBytes ( chPtr, kUTF8_PacketStart, startLen ) ) continue;
		chPtr += startLen;

		XMP_Uns8 quote = *chPtr;
		if ( (quote != '\'') && (quote != '"') ) continue;
		chPtr += 1;
		if ( *chPtr != quote ) {
			if ( ! CheckBytes ( chPtr, Uns8Ptr("\xEF\xBB\xBF"), 3 ) ) continue;
			chPtr += 3;
		}
		if ( *chPtr != quote ) continue;
		chPtr += 1;

		if ( ! CheckBytes ( chPtr, Uns8Ptr(" id="), 4 ) ) continue;
		chPtr += 4;
		quote = *chPtr;
		if ( (quote != '\'') && (quote != '"') ) continue;
		chPtr += 1;
		if ( ! CheckBytes ( chPtr, kUTF8_PacketID, idLen ) ) continue;
		chPtr += idLen;
		if ( *chPtr != quote ) continue;
		chPtr += 1;

		// We've seen enough, it is the XMP. To fit the Basic_Handler model we need to compute the
		// total size of remaining contiguous objects, the trailingContentSize. We don't use the
		// size to EOF, that would wrongly include the final zero padding for 4KB alignment.

		this->xmpPrefixSize = sizeof(InDesignContigObjMarker) + 4;
		this->xmpSuffixSize = sizeof(InDesignContigObjMarker);
		packetInfo.offset = cobjPos + this->xmpPrefixSize;
		packetInfo.length = innerLength;

		XMP_Int64 tcStart = cobjPos + streamLength + (2 * sizeof(InDesignContigObjMarker));
		while ( true ) {
			if ( checkAbort && abortProc(abortArg) ) {
				XMP_Throw ( "InDesign_MetaHandler::LocateXMP - User abort", kXMPErr_UserAbort );
			}
			cobjPos += streamLength + (2 * sizeof(InDesignContigObjMarker));
			XMP_Uns32 len = fileRef->Read ( buffer, sizeof(InDesignContigObjMarker) );
			if ( len < sizeof(InDesignContigObjMarker) ) break;	// Too small, must be end of file.
			cobjHeader = (const InDesignContigObjMarker *) &buffer[0];
			if ( ! CheckBytes ( Uns8Ptr(&cobjHeader->fGUID), kINDDContigObjHeaderGUID, kInDesignGUIDSize ) ) break;	// Not a contiguous object header.
			streamLength = GetUns32LE ( (XMP_Uns8 *) &cobjHeader->fStreamLength );
		}
		this->trailingContentSize = cobjPos - tcStart;

		#if TraceInDesignHandler
			XMP_Uns32 pktOffset = (XMP_Uns32)this->packetInfo.offset;
			printf ( "Found XMP in InDesign file, offsets:\n" );
			printf ( "  CObj head %X, XMP %X, CObj tail %X, file tail %X, padding %X\n",
					 (pktOffset - this->xmpPrefixSize), pktOffset, (pktOffset + this->packetInfo.length),
					 (pktOffset + this->packetInfo.length + this->xmpSuffixSize),
					 (pktOffset + this->packetInfo.length + this->xmpSuffixSize + (XMP_Uns32)this->trailingContentSize) );
		#endif

		this->containsXMP = true;
		break;

	}

	if ( this->containsXMP ) {
		this->xmpFileOffset = packetInfo.offset;
		this->xmpFileSize = packetInfo.length;
		ReadXMPPacket ( this );
	}

}	// InDesign_MetaHandler::CacheFileData
Exemplo n.º 10
0
void FLV_MetaHandler::CacheFileData()
{
	XMP_Assert ( ! this->containsXMP );

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

	XMP_IO* fileRef  = this->parent->ioRef;
	XMP_Uns64   fileSize = fileRef->Length();

	XMP_Uns8  buffer [16];	// Enough for 1+2+"onMetaData"+nul.
	XMP_Uns32 ioCount;
	TagInfo   info;

	fileRef->Seek ( 5, kXMP_SeekFromStart );
	fileRef->ReadAll ( buffer, 4 );

	this->flvHeaderLen = GetUns32BE ( &buffer[0] );
	XMP_Uns32 firstTagPos = this->flvHeaderLen + 4;	// Include the initial zero back pointer.

	if ( firstTagPos >= fileSize ) return;	// Quit now if the file is just a header.

	for ( XMP_Uns64 tagPos = firstTagPos; tagPos < fileSize; tagPos += (11 + info.dataSize + 4) ) {

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

		GetTagInfo ( fileRef, tagPos, &info );	// ! GetTagInfo seeks to the tag offset.
		if ( info.time != 0 ) break;
		if ( info.type != 18 ) continue;

		XMP_Assert ( sizeof(buffer) >= (1+2+10+1) );	// 02 000B onMetaData 00
		ioCount = fileRef->Read ( buffer, sizeof(buffer) );
		if ( (ioCount < 4) || (buffer[0] != 0x02) ) continue;

		XMP_Uns16     nameLen = GetUns16BE ( &buffer[1] );
		XMP_StringPtr namePtr = (XMP_StringPtr)(&buffer[3]);

		if ( this->onXMP.empty() && CheckName ( namePtr, nameLen, "onXMPData", 9 ) ) {

			// ! Put the raw data in onXMPData, analyze the value in ProcessXMP.

			this->xmpTagPos = tagPos;
			this->xmpTagLen = 11 + info.dataSize + 4;	// ! Includes the trailing back pointer.

			this->packetInfo.offset = tagPos + 11 + 1+2+nameLen;	// ! Not the real offset yet, the offset of the onXMPData value.

			ioCount = info.dataSize - (1+2+nameLen);	// Just the onXMPData value portion.
			this->onXMP.reserve ( ioCount );
			this->onXMP.assign ( ioCount, ' ' );
			fileRef->Seek ( this->packetInfo.offset, kXMP_SeekFromStart );
			fileRef->ReadAll ( (void*)this->onXMP.data(), ioCount );

			if ( ! this->onMetaData.empty() ) break;	// Done if we've found both.

		} else if ( this->onMetaData.empty() && CheckName ( namePtr, nameLen, "onMetaData", 10 ) ) {

			this->omdTagPos  = tagPos;
			this->omdTagLen  = 11 + info.dataSize + 4;	// ! Includes the trailing back pointer.

			ioCount = info.dataSize - (1+2+nameLen);	// Just the onMetaData value portion.
			this->onMetaData.reserve ( ioCount );
			this->onMetaData.assign ( ioCount, ' ' );
			fileRef->Seek ( (tagPos + 11 + 1+2+nameLen), kXMP_SeekFromStart );
			fileRef->ReadAll ( (void*)this->onMetaData.data(), ioCount );

			if ( ! this->onXMP.empty() ) break;	// Done if we've found both.

		}

	}

}	// FLV_MetaHandler::CacheFileData