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
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
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
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
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