Exemplo n.º 1
0
void GIF_MetaHandler::UpdateFile ( bool doSafeUpdate )
{
	IgnoreParam(doSafeUpdate);
	XMP_Assert( !doSafeUpdate );	// This should only be called for "unsafe" updates.

	if ( ! this->needsUpdate ) return;

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

	/*XMP_StringPtr packetStr = xmpPacket.c_str();*/
	XMP_StringLen newPacketLength = (XMP_StringLen)xmpPacket.size();

	if ( newPacketLength == XMPPacketLength )
	{
		this->SeekFile( fileRef, this->packetInfo.offset, kXMP_SeekFromStart );
		fileRef->Write( this->xmpPacket.c_str(), newPacketLength );
	}
	else
	{
		XMP_IO* tempFile = fileRef->DeriveTemp();
		if ( tempFile == 0 ) XMP_Throw( "Failure creating GIF temp file", kXMPErr_InternalFailure );

		this->WriteTempFile( tempFile );
		fileRef->AbsorbTemp();
	}

	this->needsUpdate = false;

}	// GIF_MetaHandler::UpdateFile
Exemplo n.º 2
0
void FLV_MetaHandler::UpdateFile ( bool doSafeUpdate )
{
	if ( ! this->needsUpdate ) return;
	XMP_Assert ( ! doSafeUpdate );	// This should only be called for "unsafe" updates.

	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();

	// Make sure the XMP has a legacy digest if appropriate.

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

		std::string newDigest;
		this->MakeLegacyDigest ( &newDigest );
		this->xmpObj.SetStructField ( kXMP_NS_XMP, "NativeDigests",
									  kXMP_NS_XMP, "FLV", newDigest.c_str(), kXMP_DeleteExisting );

		try {
			XMP_StringLen xmpLen = (XMP_StringLen)this->xmpPacket.size();
			this->xmpObj.SerializeToBuffer ( &this->xmpPacket, (kXMP_UseCompactFormat | kXMP_ExactPacketLength), xmpLen );
		} catch ( ... ) {
			this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_UseCompactFormat );
		}

	}

	// Rewrite the packet in-place if it fits. Otherwise rewrite the whole file.

	if ( this->xmpPacket.size() == (size_t)this->packetInfo.length ) {
		XMP_ProgressTracker* progressTracker = this->parent->progressTracker;
		if ( progressTracker != 0 ) progressTracker->BeginWork ( (float)this->xmpPacket.size() );
		fileRef->Seek ( this->packetInfo.offset, kXMP_SeekFromStart );
		fileRef->Write ( this->xmpPacket.data(), (XMP_Int32)this->xmpPacket.size() );
		if ( progressTracker != 0 ) progressTracker->WorkComplete();


	} else {

		XMP_IO* tempRef = fileRef->DeriveTemp();
		if ( tempRef == 0 ) XMP_Throw ( "Failure creating FLV temp file", kXMPErr_InternalFailure );

		this->WriteTempFile ( tempRef );
		fileRef->AbsorbTemp();

	}

	this->needsUpdate = false;

}	// FLV_MetaHandler::UpdateFile
Exemplo n.º 3
0
void Trivial_MetaHandler::UpdateFile ( bool doSafeUpdate )
{
	IgnoreParam ( doSafeUpdate );
	XMP_Assert ( ! doSafeUpdate );	// Not supported at this level.
	if ( ! this->needsUpdate ) return;

	XMP_IO*      fileRef    = this->parent->ioRef;
	XMP_PacketInfo & packetInfo = this->packetInfo;
	std::string &    xmpPacket  = this->xmpPacket;

	fileRef->Seek ( packetInfo.offset, kXMP_SeekFromStart );
	fileRef->Write ( xmpPacket.c_str(), packetInfo.length );
	XMP_Assert ( xmpPacket.size() == (size_t)packetInfo.length );

	this->needsUpdate = false;

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

	XMP_IO*   destRef    = this->parent->ioRef;
	XMP_AbortProc abortProc  = this->parent->abortProc;
	void *        abortArg   = this->parent->abortArg;

	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_TIFFFile, &this->xmpObj, &this->tiffMgr, 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 an XMP packet in the file.
	//	- The are no changes to the legacy tags. (The IPTC and PSIR are in the TIFF tags.)
	//	- The new XMP can fit in the old space.

	bool doInPlace = (fileHadXMP && (this->xmpPacket.size() <= (size_t)oldPacketLength));
	if ( this->tiffMgr.IsLegacyChanged() ) doInPlace = false;
	
	bool localProgressTracking = false;
	XMP_ProgressTracker* progressTracker = this->parent->progressTracker;

	if ( ! doInPlace ) {

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

		if ( (progressTracker != 0) && (! progressTracker->WorkInProgress()) ) {
			localProgressTracking = true;
			progressTracker->BeginWork();
		}

		this->tiffMgr.SetTag ( kTIFF_PrimaryIFD, kTIFF_XMP, kTIFF_UndefinedType, (XMP_Uns32)this->xmpPacket.size(), this->xmpPacket.c_str() );
		this->tiffMgr.UpdateFileStream ( destRef, progressTracker );

	} else {

		#if GatherPerformanceData
			sAPIPerf->back().extraInfo += ", TIFF 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;

		XMP_Assert ( this->xmpPacket.size() == (size_t)oldPacketLength );	// ! Done by common PutXMP logic.

		if ( progressTracker != 0 ) {
			if ( progressTracker->WorkInProgress() ) {
				progressTracker->AddTotalWork ( this->xmpPacket.size() );
			} else {
				localProgressTracking = true;
				progressTracker->BeginWork ( this->xmpPacket.size() );
			}
		}

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

	}
	
	if ( localProgressTracking ) progressTracker->WorkComplete();
	this->needsUpdate = false;

}	// TIFF_MetaHandler::UpdateFile
Exemplo n.º 5
0
void SVG_MetaHandler::UpdateFile( bool doSafeUpdate )
{
	XMP_Assert( !doSafeUpdate );	// This should only be called for "unsafe" updates.
	
	XMP_IO* sourceRef = this->parent->ioRef;

	if ( sourceRef == NULL || svgNode == NULL )
		return;

	// Checking whether Title updation requires or not
	std::string title;
	XML_NodePtr titleNode = svgNode->GetNamedElement( svgNode->ns.c_str(), "title" );
	(void) this->xmpObj.GetLocalizedText( kXMP_NS_DC, "title", "", "x-default", 0, &title, 0 );
	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;

	// Checking whether Description updation requires or not
	std::string description;
	XML_NodePtr descNode = svgNode->GetNamedElement( svgNode->ns.c_str(), "desc" );
	( void ) this->xmpObj.GetLocalizedText( kXMP_NS_DC, "description", "", "x-default", 0, &description, 0 );
	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;

	//	If any updation is required then don't do inplace replace
	bool isUpdateRequire = isTitleUpdateReq | isDescUpdateReq | (this->packetInfo.offset == kXMPFiles_UnknownOffset);

	// Inplace Updation of XMP
	if ( !isUpdateRequire && this->xmpPacket.size() == this->packetInfo.length )
	{
		sourceRef->Seek( this->packetInfo.offset, kXMP_SeekFromStart );
		sourceRef->Write( this->xmpPacket.c_str(), static_cast< int >( this->xmpPacket.size() ) );
	}
	else
	{
		// Inplace is not possibe, So perform full updation
		try
		{
			XMP_IO* tempRef = sourceRef->DeriveTemp();
			this->WriteTempFile( tempRef );
		}
		catch ( ... )
		{
			sourceRef->DeleteTemp();
			throw;
		}

		sourceRef->AbsorbTemp();
	}

	this->needsUpdate = false;

}	// SVG_MetaHandler::UpdateFile
Exemplo n.º 6
0
void PSD_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_PhotoshopFile, &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 an XMP packet in the file.
	//	- The are no changes to the legacy image resources. (The IPTC and EXIF are in the PSIR.)
	//	- The new XMP can fit in the old space.

	bool doInPlace = (fileHadXMP && (this->xmpPacket.size() <= (size_t)oldPacketLength));
	if ( this->psirMgr.IsLegacyChanged() ) doInPlace = false;
	XMP_ProgressTracker* progressTracker = this->parent->progressTracker;

	if ( doInPlace ) {

		#if GatherPerformanceData
			sAPIPerf->back().extraInfo += ", PSD 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;

		XMP_Assert ( this->xmpPacket.size() == (size_t)oldPacketLength );	// ! Done by common PutXMP logic.

		if ( progressTracker != 0 ) progressTracker->BeginWork ( this->xmpPacket.size() );
		liveFile->Seek ( oldPacketOffset, kXMP_SeekFromStart  );
		liveFile->Write ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() );
		if ( progressTracker != 0 ) progressTracker->WorkComplete();

	} else {

		#if GatherPerformanceData
			sAPIPerf->back().extraInfo += ", PSD 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;

}	// PSD_MetaHandler::UpdateFile
// =================================================================================================
// MP3_MetaHandler::UpdateFile
// ===========================
void MP3_MetaHandler::UpdateFile ( bool doSafeUpdate )
{
    if ( doSafeUpdate ) XMP_Throw ( "MP3_MetaHandler::UpdateFile: Safe update not supported", kXMPErr_Unavailable );

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

    // leave 2.3 resp. 2.4 header, since we want to let alone
    // and don't know enough about the encoding of unrelated frames...
    XMP_Assert( this->containsXMP );

    tagIsDirty = false;
    mustShift = false;

    // write out native properties:
    // * update existing ones
    // * create new frames as needed
    // * delete frames if property is gone!
    // see what there is to do for us:

    // RECON LOOP START
    for (int r = 0; reconProps[r].mainID != 0; r++ ) {

        std::string value;
        bool needDescriptor = false;
        bool needEncodingByte = true;

        XMP_Uns32 logicalID = GetUns32BE ( reconProps[r].mainID );
        XMP_Uns32 storedID = logicalID;
        if ( this->majorVersion == 2 ) storedID = GetUns32BE ( reconProps[r].v22ID );

        ID3v2Frame* frame = framesMap[ storedID ];	// the actual frame (if already existing)

        // get XMP property
        //	* honour specific exceptions
        //  * leave value empty() if it doesn't exist ==> frame must be delete/not created
        switch ( logicalID ) {

        case 0x54434D50: // TCMP if exists: part of compilation
            if ( xmpObj.GetProperty( kXMP_NS_DM, "partOfCompilation", &value, 0 ) && ( 0 == stricmp( value.c_str(), "true" ) )) {
                value = "1"; // set a TCMP frame of value 1
            } else {
                value.erase(); // delete/prevent creation of frame
            }
            break;

        case 0x54495432: // TIT2 -> title["x-default"]
        case 0x54434F50: // TCOP -> rights["x-default"]
            if (! xmpObj.GetLocalizedText( reconProps[r].ns, reconProps[r].prop, "", "x-default", 0, &value, 0 )) value.erase(); // if not, erase string.
            break;

        case 0x54434F4E: // TCON -> genre
        {
            bool found = xmpObj.GetProperty ( reconProps[r].ns, reconProps[r].prop, &value, 0 );
            if ( found ) {
                std::string xmpValue = value;
                ID3_Support::GenreUtils::ConvertGenreToID3 ( xmpValue.c_str(), &value );
            }
        }
        break;

        case 0x434F4D4D: // COMM
        case 0x55534C54: // USLT, both need descriptor.
            needDescriptor = true;
            if (! xmpObj.GetProperty( reconProps[r].ns, reconProps[r].prop, &value, 0 )) value.erase();
            break;

        case 0x54594552: //TYER
        case 0x54444154: //TDAT
        case 0x54494D45: //TIME
        {
            if ( majorVersion <= 3 ) {	// TYER, TIME and TDAT depricated since v. 2.4 -> else use TDRC

                XMP_DateTime dateTime;
                if (! xmpObj.GetProperty_Date( reconProps[r].ns, reconProps[r].prop, &dateTime, 0 )) {	// nothing found? -> Erase string. (Leads to Unset below)
                    value.erase();
                    break;
                }

                // TYER
                if ( logicalID == 0x54594552 ) {
                    XMP_Validate( dateTime.year <= 9999 && dateTime.year > 0, "Year is out of range", kXMPErr_BadParam);
                    // get only Year!
                    SXMPUtils::ConvertFromInt( dateTime.year, "", &value );
                    break;
                } else if ( logicalID == 0x54444154 && dateTime.hasDate ) {
                    std::string day, month;
                    SXMPUtils::ConvertFromInt( dateTime.day, "", &day );
                    SXMPUtils::ConvertFromInt( dateTime.month, "", &month );
                    if ( dateTime.day < 10 )
                        value = "0";
                    value += day;
                    if ( dateTime.month < 10 )
                        value += "0";
                    value += month;
                    break;
                } else if ( logicalID == 0x54494D45 && dateTime.hasTime ) {
                    std::string hour, minute;
                    SXMPUtils::ConvertFromInt( dateTime.hour, "", &hour );
                    SXMPUtils::ConvertFromInt( dateTime.minute, "", &minute );
                    if ( dateTime.hour < 10 )
                        value = "0";
                    value += hour;
                    if ( dateTime.minute < 10 )
                        value += "0";
                    value += minute;
                    break;
                } else {
                    value.erase();
                    break;
                }
            } else {
                value.erase();
                break;
            }
        }
        break;

        case 0x54445243: //TDRC (only v2.4)
        {
            // only export for id3 > v2.4
            if ( majorVersion > 3 )  {
                if (! xmpObj.GetProperty( reconProps[r].ns, reconProps[r].prop, &value, 0 )) value.erase();
            }
            break;
        }
        break;

        case 0x57434F50: //WCOP
            needEncodingByte = false;
            if (! xmpObj.GetProperty( reconProps[r].ns, reconProps[r].prop, &value, 0 )) value.erase(); // if not, erase string
            break;

        case 0x5452434B: // TRCK
        case 0x54504F53: // TPOS
        // no break, go on:

        default:
            if (! xmpObj.GetProperty( reconProps[r].ns, reconProps[r].prop, &value, 0 )) value.erase(); // if not, erase string
            break;

        }

        // [XMP exist] x [frame exist] => four cases:
        // 1/4) nothing before, nothing now
        if ( value.empty() && (frame==0)) continue; // nothing to do

        // all else means there will be rewrite work to do:
        tagIsDirty = true;

        // 2/4) value before, now gone:
        if ( value.empty() && (frame!=0)) {
            frame->active = false; //mark for non-use
            continue;
        }

        // 3/4) no old value, create new value
        bool needUTF16 = false;
        if ( needEncodingByte ) needUTF16 = (! ReconcileUtils::IsASCII ( value.c_str(), value.size() ) );
        if ( frame != 0 ) {
            frame->setFrameValue( value, needDescriptor, needUTF16, false, needEncodingByte );
        } else {
            ID3v2Frame* newFrame=new ID3v2Frame( storedID );
            newFrame->setFrameValue( value, needDescriptor,  needUTF16, false, needEncodingByte ); //always write as utf16-le incl. BOM
            framesVector.push_back( newFrame );
            framesMap[ storedID ] = newFrame;
            continue;
        }

    } 	// RECON LOOP END

    /////////////////////////////////////////////////////////////////////////////////
    // (Re)Build XMP frame:

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

    ID3v2Frame* frame = framesMap[ xmpID ];
    if ( frame != 0 ) {
        frame->setFrameValue( this->xmpPacket, false, false, true );
    } else {
        ID3v2Frame* newFrame=new ID3v2Frame( xmpID );
        newFrame->setFrameValue ( this->xmpPacket, false, false, true );
        framesVector.push_back ( newFrame );
        framesMap[ xmpID ] = newFrame;
    }

    ////////////////////////////////////////////////////////////////////////////////
    // Decision making

    XMP_Int32 frameHeaderSize = ID3v2Frame::kV23_FrameHeaderSize;
    if ( this->majorVersion == 2 ) frameHeaderSize = ID3v2Frame::kV22_FrameHeaderSize;

    newFramesSize = 0;
    for ( XMP_Uns32 i = 0; i < framesVector.size(); i++ ) {
        if ( framesVector[i]->active ) newFramesSize += (frameHeaderSize + framesVector[i]->contentSize);
    }

    mustShift = (newFramesSize > (XMP_Int64)(oldTagSize - ID3Header::kID3_TagHeaderSize)) ||
                //optimization: If more than 8K can be saved by rewriting the MP3, go do it:
                ((newFramesSize + 8*1024) < oldTagSize );

    if ( ! mustShift )	{	// fill what we got
        newTagSize = oldTagSize;
    } else { // if need to shift anyway, get some nice 2K padding
        newTagSize = newFramesSize + 2048 + ID3Header::kID3_TagHeaderSize;
    }
    newPadding = newTagSize - ID3Header::kID3_TagHeaderSize - newFramesSize;

    // shifting needed? -> shift
    if ( mustShift ) {
        XMP_Int64 filesize = file ->Length();
        if ( this->hasID3Tag ) {
            XIO::Move ( file, oldTagSize, file, newTagSize, filesize - oldTagSize ); //fix [2338569]
        } else {
            XIO::Move ( file, 0, file, newTagSize, filesize ); // move entire file up.
        }
    }

    // correct size stuff, write out header
    file ->Rewind();
    id3Header.write ( file, newTagSize );

    // write out tags
    for ( XMP_Uns32 i = 0; i < framesVector.size(); i++ ) {
        if ( framesVector[i]->active ) framesVector[i]->write ( file, majorVersion );
    }

    // write out padding:
    for ( XMP_Int64 i = newPadding; i > 0; ) {
        const XMP_Uns64 zero = 0;
        if ( i >= 8 ) {
            file->Write ( &zero, 8  );
            i -= 8;
            continue;
        }
        file->Write ( &zero, 1  );
        i--;
    }

    // check end of file for ID3v1 tag
    XMP_Int64 possibleTruncationPoint = file->Seek ( -128, kXMP_SeekFromEnd );
    bool alreadyHasID3v1 = (XIO::ReadInt32_BE( file ) & 0xFFFFFF00) == 0x54414700; // "TAG"
    if ( ! alreadyHasID3v1 ) file->Seek ( 128, kXMP_SeekFromEnd );	// Seek will extend the file.
    id3v1Tag.write( file, &this->xmpObj );

    this->needsUpdate = false; //do last for safety reasons

}	// MP3_MetaHandler::UpdateFile