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
// b) parsing ContainerChunk::ContainerChunk( ContainerChunk* parent, RIFF_MetaHandler* handler ) : Chunk( parent, handler, false, chunk_CONTAINER ) { bool repairMode = ( 0 != ( handler->parent->openFlags & kXMPFiles_OpenRepairFile )); try { XMP_IO* file = handler->parent->ioRef; XMP_Uns8 level = handler->level; // get type of container chunk this->containerType = XIO::ReadUns32_LE( file ); // ensure legality of top-level chunks if ( level == 0 && handler->riffChunks.size() > 0 ) { XMP_Validate( handler->parent->format == kXMP_AVIFile, "only AVI may have multiple top-level chunks", kXMPErr_BadFileFormat ); XMP_Validate( this->containerType == kType_AVIX, "all chunks beyond main chunk must be type AVIX", kXMPErr_BadFileFormat ); } // has *relevant* subChunks? (there might be e.g. non-INFO LIST chunks we don't care about) bool hasSubChunks = ( ( this->id == kChunk_RIFF ) || ( this->id == kChunk_LIST && this->containerType == kType_INFO ) || ( this->id == kChunk_LIST && this->containerType == kType_Tdat ) || ( this->id == kChunk_LIST && this->containerType == kType_hdrl ) ); XMP_Int64 endOfChunk = this->oldPos + this->oldSize; // this statement catches beyond-EoF-offsets on any level // exception: level 0, tolerate if in repairMode if ( (level == 0) && repairMode && (endOfChunk > handler->oldFileSize) ) { endOfChunk = handler->oldFileSize; // assign actual file size this->oldSize = endOfChunk - this->oldPos; //reversely calculate correct oldSize } XMP_Validate( endOfChunk <= handler->oldFileSize, "offset beyond EoF", kXMPErr_BadFileFormat ); Chunk* curChild = 0; if ( hasSubChunks ) { handler->level++; while ( file->Offset() < endOfChunk ) { curChild = RIFF::getChunk( this, handler ); // digest pad byte - no value validation (0), since some 3rd party files have non-0-padding. if ( file->Offset() % 2 == 1 ) { // [1521093] tolerate missing pad byte at very end of file: XMP_Uns8 pad; file->Read ( &pad, 1 ); // Read the pad, tolerate being at EOF. } // within relevant LISTs, relentlesly delete junk chunks (create a single one // at end as part of updateAndChanges() if ( (containerType== kType_INFO || containerType == kType_Tdat) && ( curChild->chunkType == chunk_JUNK ) ) { this->children.pop_back(); delete curChild; } // for other chunks: join neighouring Junk chunks into one else if ( (curChild->chunkType == chunk_JUNK) && ( this->children.size() >= 2 ) ) { // nb: if there are e.g 2 chunks, then last one is at(1), prev one at(0) ==> '-2' Chunk* prevChunk = this->children.at( this->children.size() - 2 ); if ( prevChunk->chunkType == chunk_JUNK ) { // stack up size to prior chunk prevChunk->oldSize += curChild->oldSize; prevChunk->newSize += curChild->newSize; XMP_Enforce( prevChunk->oldSize == prevChunk->newSize ); // destroy current chunk this->children.pop_back(); delete curChild; } } } handler->level--; XMP_Validate( file->Offset() == endOfChunk, "subchunks exceed outer chunk size", kXMPErr_BadFileFormat ); // pointers for later legacy processing if ( level==1 && this->id==kChunk_LIST && this->containerType == kType_INFO ) handler->listInfoChunk = this; if ( level==1 && this->id==kChunk_LIST && this->containerType == kType_Tdat ) handler->listTdatChunk = this; if ( level == 1 && this->id == kChunk_LIST && this->containerType == kType_hdrl ) handler->listHdlrChunk = this; } else // skip non-interest container chunk { file->Seek ( (this->oldSize - 8 - 4), kXMP_SeekFromCurrent ); } // if - else } // try catch (XMP_Error& e) { this->release(); // free resources if ( this->parent != 0) this->parent->children.pop_back(); // hereby taken care of, so removing myself... throw e; // re-throw } }
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 JPEG_MetaHandler::CacheFileData() { XMP_IO* fileRef = this->parent->ioRef; XMP_PacketInfo & packetInfo = this->packetInfo; static const size_t kBufferSize = 64*1024; // Enough for maximum segment contents. XMP_Uns8 buffer [kBufferSize]; psirContents.clear(); exifContents.clear(); XMP_AbortProc abortProc = this->parent->abortProc; void * abortArg = this->parent->abortArg; const bool checkAbort = (abortProc != 0); ExtendedXMPInfo extXMP; XMP_Assert ( ! this->containsXMP ); // Set containsXMP to true here only if the standard XMP packet is found. XMP_Assert ( kPSIRSignatureLength == (strlen(kPSIRSignatureString) + 1) ); XMP_Assert ( kMainXMPSignatureLength == (strlen(kMainXMPSignatureString) + 1) ); XMP_Assert ( kExtXMPSignatureLength == (strlen(kExtXMPSignatureString) + 1) ); // ------------------------------------------------------------------------------------------- // Look for any of the Exif, PSIR, main XMP, or extended XMP marker segments. Quit when we hit // an SOFn, EOI, or invalid/unexpected marker. fileRef->Seek ( 2, kXMP_SeekFromStart ); // Skip the SOI, CheckFormat made sure it is present. while ( true ) { if ( checkAbort && abortProc(abortArg) ) { XMP_Throw ( "JPEG_MetaHandler::CacheFileData - User abort", kXMPErr_UserAbort ); } if ( ! XIO::CheckFileSpace ( fileRef, 2 ) ) return; // Quit, don't throw, if the file ends unexpectedly. XMP_Uns16 marker = XIO::ReadUns16_BE ( fileRef ); // Read the next marker. if ( marker == 0xFFFF ) { // Have a pad byte, skip it. These are almost unheard of, so efficiency isn't critical. fileRef->Seek ( -1, kXMP_SeekFromCurrent ); // Skip the first 0xFF, read the second again. continue; } if ( (marker == 0xFFDA) || (marker == 0xFFD9) ) break; // Quit reading at the first SOS marker or at EOI. if ( (marker == 0xFF01) || // Ill-formed file if we encounter a TEM or RSTn marker. ((0xFFD0 <= marker) && (marker <= 0xFFD7)) ) return; XMP_Uns16 contentLen = XIO::ReadUns16_BE ( fileRef ); // Read this segment's length. if ( contentLen < 2 ) XMP_Throw ( "Invalid JPEG segment length", kXMPErr_BadJPEG ); contentLen -= 2; // Reduce to just the content length. XMP_Int64 contentOrigin = fileRef->Offset(); size_t signatureLen; if ( (marker == 0xFFED) && (contentLen >= kPSIRSignatureLength) ) { // This is an APP13 marker, is it the Photoshop image resources? signatureLen = fileRef->Read ( buffer, kPSIRSignatureLength ); if ( (signatureLen == kPSIRSignatureLength) && CheckBytes ( &buffer[0], kPSIRSignatureString, kPSIRSignatureLength ) ) { size_t psirLen = contentLen - kPSIRSignatureLength; fileRef->Seek ( (contentOrigin + kPSIRSignatureLength), kXMP_SeekFromStart ); fileRef->ReadAll ( buffer, psirLen ); this->psirContents.append( (char *) buffer, psirLen ); continue; // Move on to the next marker. } } else if ( (marker == 0xFFE1) && (contentLen >= kExifSignatureLength) ) { // Check for the shortest signature. // This is an APP1 marker, is it the Exif, main XMP, or extended XMP? // ! Check in that order, which is in increasing signature string length. XMP_Assert ( (kExifSignatureLength < kMainXMPSignatureLength) && (kMainXMPSignatureLength < kExtXMPSignatureLength) ); signatureLen = fileRef->Read ( buffer, kExtXMPSignatureLength ); // Read for the longest signature. if ( (signatureLen >= kExifSignatureLength) && (CheckBytes ( &buffer[0], kExifSignatureString, kExifSignatureLength ) || CheckBytes ( &buffer[0], kExifSignatureAltStr, kExifSignatureLength )) ) { size_t exifLen = contentLen - kExifSignatureLength; fileRef->Seek ( (contentOrigin + kExifSignatureLength), kXMP_SeekFromStart ); fileRef->ReadAll ( buffer, exifLen ); this->exifContents.append ( (char*)buffer, exifLen ); continue; // Move on to the next marker. } if ( (signatureLen >= kMainXMPSignatureLength) && CheckBytes ( &buffer[0], kMainXMPSignatureString, kMainXMPSignatureLength ) ) { this->containsXMP = true; // Found the standard XMP packet. size_t xmpLen = contentLen - kMainXMPSignatureLength; fileRef->Seek ( (contentOrigin + kMainXMPSignatureLength), kXMP_SeekFromStart ); fileRef->ReadAll ( buffer, xmpLen ); this->xmpPacket.assign ( (char*)buffer, xmpLen ); this->packetInfo.offset = contentOrigin + kMainXMPSignatureLength; this->packetInfo.length = (XMP_Int32)xmpLen; this->packetInfo.padSize = 0; // Assume the rest for now, set later in ProcessXMP. this->packetInfo.charForm = kXMP_CharUnknown; this->packetInfo.writeable = true; continue; // Move on to the next marker. } if ( (signatureLen >= kExtXMPSignatureLength) && CheckBytes ( &buffer[0], kExtXMPSignatureString, kExtXMPSignatureLength ) ) { fileRef->Seek ( contentOrigin, kXMP_SeekFromStart ); fileRef->ReadAll ( buffer, contentLen ); CacheExtendedXMP ( &extXMP, buffer, contentLen ); continue; // Move on to the next marker. } } // None of the above, seek to the next marker. fileRef->Seek ( (contentOrigin + contentLen) , kXMP_SeekFromStart ); } if ( ! extXMP.empty() ) { // We have extended XMP. Find out which ones are complete, collapse them into a single // string, and save them for ProcessXMP. ExtendedXMPInfo::iterator guidPos = extXMP.begin(); ExtendedXMPInfo::iterator guidEnd = extXMP.end(); for ( ; guidPos != guidEnd; ++guidPos ) { ExtXMPContent & thisContent = guidPos->second; ExtXMPPortions::iterator partZero = thisContent.portions.begin(); ExtXMPPortions::iterator partEnd = thisContent.portions.end(); ExtXMPPortions::iterator partPos = partZero; #if Trace_UnlimitedJPEG printf ( "Extended XMP portions for GUID %.32s, full length %d\n", guidPos->first.data, guidPos->second.length ); printf ( " Offset %d, length %d, next offset %d\n", partZero->first, partZero->second.size(), (partZero->first + partZero->second.size()) ); #endif for ( ++partPos; partPos != partEnd; ++partPos ) { #if Trace_UnlimitedJPEG printf ( " Offset %d, length %d, next offset %d\n", partPos->first, partPos->second.size(), (partPos->first + partPos->second.size()) ); #endif if ( partPos->first != partZero->second.size() ) break; // Quit if not contiguous. partZero->second.append ( partPos->second ); } if ( (partPos == partEnd) && (partZero->first == 0) && (partZero->second.size() == thisContent.length) ) { // This is a complete extended XMP stream. this->extendedXMP.insert ( ExtendedXMPMap::value_type ( guidPos->first, partZero->second ) ); #if Trace_UnlimitedJPEG printf ( "Full extended XMP for GUID %.32s, full length %d\n", guidPos->first.data, partZero->second.size() ); #endif } } } } // JPEG_MetaHandler::CacheFileData
void 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::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