void PSD_MetaHandler::CacheFileData() { XMP_IO* fileRef = this->parent->ioRef; XMP_PacketInfo & packetInfo = this->packetInfo; XMP_AbortProc abortProc = this->parent->abortProc; void * abortArg = this->parent->abortArg; const bool checkAbort = (abortProc != 0); XMP_Assert ( ! this->containsXMP ); // Set containsXMP to true here only if the XMP image resource is found. if ( checkAbort && abortProc(abortArg) ) { XMP_Throw ( "PSD_MetaHandler::CacheFileData - User abort", kXMPErr_UserAbort ); } XMP_Uns8 psdHeader[30]; XMP_Uns32 ioLen, cmLen; XMP_Int64 filePos = 0; fileRef->Rewind ( ); ioLen = fileRef->Read ( psdHeader, 30 ); if ( ioLen != 30 ) return; // Throw? this->imageHeight = GetUns32BE ( &psdHeader[14] ); this->imageWidth = GetUns32BE ( &psdHeader[18] ); cmLen = GetUns32BE ( &psdHeader[26] ); XMP_Int64 psirOrigin = 26 + 4 + cmLen; filePos = fileRef->Seek ( psirOrigin, kXMP_SeekFromStart ); if ( filePos != psirOrigin ) return; // Throw? if ( ! XIO::CheckFileSpace ( fileRef, 4 ) ) return; // Throw? XMP_Uns32 psirLen = XIO::ReadUns32_BE ( fileRef ); this->psirMgr.ParseFileResources ( fileRef, psirLen ); PSIR_Manager::ImgRsrcInfo xmpInfo; bool found = this->psirMgr.GetImgRsrc ( kPSIR_XMP, &xmpInfo ); if ( found ) { // printf ( "PSD_MetaHandler::CacheFileData - XMP packet offset %d (0x%X), size %d\n", // xmpInfo.origOffset, xmpInfo.origOffset, xmpInfo.dataLen ); this->packetInfo.offset = xmpInfo.origOffset; this->packetInfo.length = xmpInfo.dataLen; this->packetInfo.padSize = 0; // Assume for now, set these properly in ProcessXMP. this->packetInfo.charForm = kXMP_CharUnknown; this->packetInfo.writeable = true; this->xmpPacket.assign ( (XMP_StringPtr)xmpInfo.dataPtr, xmpInfo.dataLen ); this->containsXMP = true; } } // PSD_MetaHandler::CacheFileData
void TIFF_MetaHandler::WriteTempFile ( XMP_IO* tempRef ) { XMP_IO* origRef = this->parent->ioRef; XMP_AbortProc abortProc = this->parent->abortProc; void * abortArg = this->parent->abortArg; XMP_Int64 fileLen = origRef->Length(); if ( fileLen > 0xFFFFFFFFLL ) { // Check before making a copy of the file. XMP_Throw ( "TIFF fles can't exceed 4GB", kXMPErr_BadTIFF ); } XMP_ProgressTracker* progressTracker = this->parent->progressTracker; if ( progressTracker != 0 ) progressTracker->BeginWork ( (float)fileLen ); origRef->Rewind ( ); tempRef->Truncate ( 0 ); XIO::Copy ( origRef, tempRef, fileLen, abortProc, abortArg ); try { this->parent->ioRef = tempRef; // ! Make UpdateFile update the temp. this->UpdateFile ( false ); this->parent->ioRef = origRef; } catch ( ... ) { this->parent->ioRef = origRef; throw; } if ( progressTracker != 0 ) progressTracker->WorkComplete(); } // TIFF_MetaHandler::WriteTempFile
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 Matroska_MetaHandler::WriteTempFile(XMP_IO* tempRef) { XMP_Assert(needsUpdate); XMP_IO* originalRef = parent->ioRef; bool localProgressTracking(false); XMP_ProgressTracker* progressTracker = parent->progressTracker; if (progressTracker) { float xmpSize = static_cast<float>(xmpPacket.size()); if (progressTracker->WorkInProgress()) { progressTracker->AddTotalWork(xmpSize); } else { localProgressTracking = true; progressTracker->BeginWork(xmpSize); } } XMP_Assert(tempRef); XMP_Assert(originalRef); tempRef->Rewind(); originalRef->Rewind(); XIO::Copy(originalRef, tempRef, originalRef->Length(), parent->abortProc, parent->abortArg); try { parent->ioRef = tempRef; // ! Fool UpdateFile into using the temp file. UpdateFile(false); parent->ioRef = originalRef; } catch (...) { parent->ioRef = originalRef; throw; } if (localProgressTracking) progressTracker->WorkComplete(); }
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); }
// ================================================================================================= // 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
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 PSD_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_ProgressTracker* progressTracker = this->parent->progressTracker; XMP_Uns64 sourceLen = origRef->Length(); if ( sourceLen == 0 ) return; // Tolerate empty files. // Reconcile the legacy metadata, unless this is called from UpdateFile. Reserialize the XMP to // get standard padding, PutXMP has probably done an in-place serialize. Set the XMP image resource. 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 ); } this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_UseCompactFormat ); this->packetInfo.offset = kXMPFiles_UnknownOffset; this->packetInfo.length = (XMP_StringLen)this->xmpPacket.size(); FillPacketInfo ( this->xmpPacket, &this->packetInfo ); this->psirMgr.SetImgRsrc ( kPSIR_XMP, this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() ); // Calculate the total writes I/O to be done by this method. This includes header section, color // mode section and tail length after the image resources section. The write I/O for image // resources section is added to total work in PSIR_FileWriter::UpdateFileResources. origRef->Seek ( 26, kXMP_SeekFromStart ); //move to the point after Header 26 is the header length XMP_Uns32 cmLen,cmLen1; origRef->Read ( &cmLen, 4 ); // get the length of color mode section cmLen1 = GetUns32BE ( &cmLen ); origRef->Seek ( cmLen1, kXMP_SeekFromCurrent ); //move to the end of color mode section XMP_Uns32 irLen; origRef->Read ( &irLen, 4 ); // Get the source image resource section length. irLen = GetUns32BE ( &irLen ); XMP_Uns64 tailOffset = 26 + 4 + cmLen1 + 4 + irLen; XMP_Uns64 tailLength = sourceLen - tailOffset; // Add work for 26 bytes header, 4 bytes color mode section length, color mode section length // and tail length after the image resources section length. if ( progressTracker != 0 ) progressTracker->BeginWork ( (float)(26.0f + 4.0f + cmLen1 + tailLength) ); // Copy the file header and color mode section, then write the updated image resource section, // and copy the tail of the source file (layer and mask section to EOF). origRef->Rewind ( ); tempRef->Truncate ( 0 ); XIO::Copy ( origRef, tempRef, 26 ); // Copy the file header. origRef->Seek ( 4, kXMP_SeekFromCurrent ); tempRef->Write ( &cmLen, 4 ); // Copy the color mode section length. XIO::Copy ( origRef, tempRef, cmLen1 ); // Copy the color mode section contents. this->psirMgr.UpdateFileResources ( origRef, tempRef, abortProc, abortArg ,progressTracker ); origRef->Seek ( tailOffset, kXMP_SeekFromStart ); tempRef->Seek ( 0, kXMP_SeekFromEnd ); XIO::Copy ( origRef, tempRef, tailLength ); // Copy the tail of the file. this->needsUpdate = false; if ( progressTracker != 0 ) progressTracker->WorkComplete(); } // PSD_MetaHandler::WriteTempFile
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
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
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
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
void FLV_MetaHandler::WriteTempFile ( XMP_IO* tempRef ) { if ( ! this->needsUpdate ) return; XMP_AbortProc abortProc = this->parent->abortProc; void * abortArg = this->parent->abortArg; const bool checkAbort = (abortProc != 0); XMP_IO* originalRef = this->parent->ioRef; XMP_Uns64 sourceLen = originalRef->Length(); XMP_Uns64 sourcePos = 0; originalRef->Rewind(); tempRef->Rewind(); tempRef->Truncate ( 0 ); XMP_ProgressTracker* progressTracker = this->parent->progressTracker; if ( progressTracker != 0 ) { float fileSize=(float)(this->xmpPacket.size()+48); if ( this->omdTagPos == 0 ) { sourcePos=(this->flvHeaderLen+4); fileSize+=sourcePos; } else { if ( this->xmpTagPos < this->omdTagPos ) { fileSize+=this->xmpTagPos; } fileSize+=(this->omdTagPos + this->omdTagLen- ((this->xmpTagPos!=0 && this->xmpTagPos < this->omdTagPos)? (this->xmpTagPos + this->xmpTagLen):0)); sourcePos =this->omdTagPos + this->omdTagLen; } if ( (this->xmpTagPos != 0) && (this->xmpTagPos >= sourcePos) ) { fileSize+=(this->xmpTagPos - sourcePos); sourcePos=this->xmpTagPos + this->xmpTagLen; } fileSize+=(sourceLen - sourcePos); sourcePos=0; progressTracker->BeginWork ( fileSize ); } // First do whatever is needed to put the new XMP after any existing onMetaData tag, or as the // first time 0 tag. if ( this->omdTagPos == 0 ) { // There is no onMetaData tag. Copy the file header, then write the new XMP as the first tag. // Allow the degenerate case of a file with just a header, no initial back pointer or tags. originalRef->Seek ( sourcePos, kXMP_SeekFromStart ); XIO::Copy ( originalRef, tempRef, this->flvHeaderLen, abortProc, abortArg ); XMP_Uns32 zero = 0; // Ensure that the initial back offset really is zero. tempRef->Write ( &zero, 4 ); sourcePos = this->flvHeaderLen + 4; WriteOnXMP ( tempRef, this->xmpPacket ); } else { // There is an onMetaData tag. Copy the front of the file through the onMetaData tag, // skipping any XMP that happens to be in the way. The XMP should not be before onMetaData, // but let's be robust. Write the new XMP immediately after onMetaData, at the same time. XMP_Uns64 omdEnd = this->omdTagPos + this->omdTagLen; if ( (this->xmpTagPos != 0) && (this->xmpTagPos < this->omdTagPos) ) { // The XMP tag was in front of the onMetaData tag. Copy up to it, then skip it. originalRef->Seek ( sourcePos, kXMP_SeekFromStart ); XIO::Copy ( originalRef, tempRef, this->xmpTagPos, abortProc, abortArg ); sourcePos = this->xmpTagPos + this->xmpTagLen; // The tag length includes the trailing size field. } // Copy through the onMetaData tag, then write the XMP. originalRef->Seek ( sourcePos, kXMP_SeekFromStart ); XIO::Copy ( originalRef, tempRef, (omdEnd - sourcePos), abortProc, abortArg ); sourcePos = omdEnd; WriteOnXMP ( tempRef, this->xmpPacket ); } // Copy the rest of the file, skipping any XMP that is in the way. if ( (this->xmpTagPos != 0) && (this->xmpTagPos >= sourcePos) ) { originalRef->Seek ( sourcePos, kXMP_SeekFromStart ); XIO::Copy ( originalRef, tempRef, (this->xmpTagPos - sourcePos), abortProc, abortArg ); sourcePos = this->xmpTagPos + this->xmpTagLen; } originalRef->Seek ( sourcePos, kXMP_SeekFromStart ); XIO::Copy ( originalRef, tempRef, (sourceLen - sourcePos), abortProc, abortArg ); this->needsUpdate = false; if ( progressTracker != 0 ) progressTracker->WorkComplete(); } // FLV_MetaHandler::WriteTempFile