void RIFF_MetaHandler::CacheFileData() { this->containsXMP = false; //assume for now XMP_IO* file = this->parent->ioRef; this->oldFileSize = file ->Length(); if ( (this->parent->format == kXMP_WAVFile) && (this->oldFileSize > 0xFFFFFFFF) ) XMP_Throw ( "RIFF_MetaHandler::CacheFileData: WAV Files larger 4GB not supported", kXMPErr_Unimplemented ); file ->Rewind(); this->level = 0; // parse top-level chunks (most likely just one, except large avi files) XMP_Int64 filePos = 0; while ( filePos < this->oldFileSize ) { this->riffChunks.push_back( (RIFF::ContainerChunk*) RIFF::getChunk( NULL, this ) ); // Tolerate limited forms of trailing garbage in a file. Some apps append private data. filePos = file->Offset(); XMP_Int64 fileTail = this->oldFileSize - filePos; if ( fileTail != 0 ) { if ( fileTail < 12 ) { this->oldFileSize = filePos; // Pretend the file is smaller. this->trailingGarbageSize = fileTail; } else if ( this->parent->format == kXMP_WAVFile ) { if ( fileTail < 1024*1024 ) { this->oldFileSize = filePos; // Pretend the file is smaller. this->trailingGarbageSize = fileTail; } else { XMP_Throw ( "Excessive garbage at end of file", kXMPErr_BadFileFormat ) } } else { XMP_Int32 chunkInfo [3]; file->ReadAll ( &chunkInfo, 12 ); file->Seek ( -12, kXMP_SeekFromCurrent ); if ( (GetUns32LE ( &chunkInfo[0] ) != RIFF::kChunk_RIFF) || (GetUns32LE ( &chunkInfo[2] ) != RIFF::kType_AVIX) ) { if ( fileTail < 1024*1024 ) { this->oldFileSize = filePos; // Pretend the file is smaller. this->trailingGarbageSize = fileTail; } else { XMP_Throw ( "Excessive garbage at end of file", kXMPErr_BadFileFormat ) } } } } }
// b) parse XMPChunk::XMPChunk( ContainerChunk* parent_, RIFF_MetaHandler* handler ) : Chunk( parent_, handler, false, chunk_XMP ) { chunkType = chunk_XMP; XMP_IO* file = handler->parent->ioRef; /*XMP_Uns8 level = handler->level*/; handler->packetInfo.offset = this->oldPos + 8; handler->packetInfo.length = (XMP_Int32) this->oldSize - 8; handler->xmpPacket.reserve ( handler->packetInfo.length ); handler->xmpPacket.assign ( handler->packetInfo.length, ' ' ); file->ReadAll ( (void*)handler->xmpPacket.data(), handler->packetInfo.length ); handler->containsXMP = true; // last, after all possible failure // pointer for later processing handler->xmpChunk = this; }
// b) parsing ValueChunk::ValueChunk( ContainerChunk* parent_, RIFF_MetaHandler* handler ) : Chunk( parent_, handler, false, chunk_VALUE ) { // set value: ----------------- XMP_IO* file = handler->parent->ioRef; /*XMP_Uns8 level = handler->level;*/ // unless changed through reconciliation, assume for now. // IMPORTANT to stay true to the original (no \0 cleanup or similar) // since unknown value chunks might not be fully understood, // hence must be precisely preserved !!! XMP_Int32 length = (XMP_Int32) this->oldSize - 8; this->oldValue.reserve( length ); this->oldValue.assign( length + 1, '\0' ); file->ReadAll ( (void*)this->oldValue.data(), length ); this->newValue = this->oldValue; this->newSize = this->oldSize; }
void GIF_MetaHandler::CacheFileData() { this->containsXMP = false; XMP_IO * fileRef = this->parent->ioRef; // Try to navigate through the blocks to find the XMP block. if ( this->ParseGIFBlocks( fileRef ) ) { // XMP packet present this->xmpPacket.assign( XMPPacketLength, ' ' ); // 13 bytes for the block size and 2 bytes for Extension ID and Label this->SeekFile( fileRef, XMPPacketOffset, kXMP_SeekFromStart ); fileRef->ReadAll( ( void* )this->xmpPacket.data(), XMPPacketLength ); this->packetInfo.offset = XMPPacketOffset; this->packetInfo.length = XMPPacketLength; this->containsXMP = true; } // else no XMP } // GIF_MetaHandler::CacheFileData
void SVG_MetaHandler::CacheFileData() { XMP_Assert( !this->containsXMP ); XMP_IO * fileRef = this->parent->ioRef; XMP_Uns8 marker[ 4 ]; fileRef->Rewind(); fileRef->Read( marker, 4 ); // Checking for UTF-16 BOM and UTF-32 BOM if ( ( marker[ 0 ] == 0xFF && marker[ 1 ] == 0xFE ) || ( marker[ 0 ] == 0xFE && marker[ 1 ] == 0xFF ) || ( marker[ 0 ] == marker[ 1 ] == 0x00 && marker[ 2 ] == 0xFE && marker[ 3 ] == 0xFF ) ) { XMP_Error error( kXMPErr_BadXML, "Invalid SVG file" ); this->NotifyClient( &this->parent->errorCallback, kXMPErrSev_OperationFatal, error ); } // Creating a new SVG Parser svgAdapter = new SVG_Adapter(); if ( svgAdapter == 0 ) XMP_Throw( "SVG_MetaHandler: Can't create SVG adapter", kXMPErr_NoMemory ); svgAdapter->SetErrorCallback( &this->parent->errorCallback ); // Registering all the required tags to SVG Parser svgAdapter->RegisterPI( "xpacket" ); svgAdapter->RegisterElement( "metadata", "svg" ); svgAdapter->RegisterElement( "xmpmeta", "metadata" ); svgAdapter->RegisterElement( "RDF", "metadata" ); svgAdapter->RegisterElement( "title", "svg" ); svgAdapter->RegisterElement( "desc", "svg" ); // Parsing the whole buffer fileRef->Rewind(); XMP_Uns8 buffer[ 64 * 1024 ]; while ( true ) { XMP_Int32 ioCount = fileRef->Read( buffer, sizeof( buffer ) ); if ( ioCount == 0 || !svgAdapter->IsParsingRequire() ) break; svgAdapter->ParseBuffer( buffer, ioCount, false /* not the end */ ); } svgAdapter->ParseBuffer( 0, 0, true ); // End the parse. XML_Node & xmlTree = this->svgAdapter->tree; XML_NodePtr rootElem = 0; for ( size_t i = 0, limit = xmlTree.content.size(); i < limit; ++i ) { if ( xmlTree.content[ i ]->kind == kElemNode ) { rootElem = xmlTree.content[ i ]; } } if ( rootElem == 0 ) XMP_Throw( "Not a valid SVG File", kXMPErr_BadFileFormat ); XMP_StringPtr rootLocalName = rootElem->name.c_str() + rootElem->nsPrefixLen; if ( ! XMP_LitMatch( rootLocalName, "svg" ) ) XMP_Throw( "Not able to parse such SVG File", kXMPErr_BadFileFormat ); // Making SVG node as Root Node svgNode = rootElem; bool FoundPI = false; bool FoundWrapper = false; XML_NodePtr metadataNode = svgNode->GetNamedElement( rootElem->ns.c_str(), "metadata" ); // We are intersted only in the Metadata tag of outer SVG element // XMP should be present only in metadata Node of SVG if ( metadataNode != NULL ) { XMP_Int64 packetLength = -1; XMP_Int64 packetOffset = -1; XMP_Int64 PIOffset = svgAdapter->GetPIOffset( "xpacket", 1 ); OffsetStruct wrapperOffset = svgAdapter->GetElementOffsets( "xmpmeta" ); OffsetStruct rdfOffset = svgAdapter->GetElementOffsets( "RDF" ); // Checking XMP PI's position if ( PIOffset != -1 ) { if ( wrapperOffset.startOffset != -1 && wrapperOffset.startOffset < PIOffset ) packetOffset = wrapperOffset.startOffset; else { XMP_Int64 trailerOffset = svgAdapter->GetPIOffset( "xpacket", 2 ); XML_NodePtr trailerNode = metadataNode->GetNamedElement( "", "xpacket", 1 ); if ( trailerOffset != -1 || trailerNode != 0 ) { packetLength = 2; // "<?" = 2 packetLength += trailerNode->name.length(); // Node's name packetLength += 1; // Empty Space after Node's name packetLength += trailerNode->value.length(); // Value packetLength += 2; // "?>" = 2 packetLength += ( trailerOffset - PIOffset ); packetOffset = PIOffset; } } } else if ( wrapperOffset.startOffset != -1 ) // XMP Wrapper is present without PI { XML_NodePtr wrapperNode = metadataNode->GetNamedElement( "adobe:ns:meta/", "xmpmeta" ); if ( wrapperNode != 0 ) { std::string trailerWrapper = "</x:xmpmeta>"; packetLength = trailerWrapper.length(); packetLength += ( wrapperOffset.endOffset - wrapperOffset.startOffset ); packetOffset = wrapperOffset.startOffset; } } else // RDF packet is present without PI and wrapper { XML_NodePtr rdfNode = metadataNode->GetNamedElement( "http://www.w3.org/1999/02/22-rdf-syntax-ns#", "RDF" ); if ( rdfNode != 0 ) { std::string rdfTrailer = "</rdf:RDF>"; packetLength = rdfTrailer.length(); packetLength += ( rdfOffset.endOffset - rdfOffset.startOffset ); packetOffset = rdfOffset.startOffset; } } // Fill the necesarry information and packet with XMP data if ( packetOffset != -1 ) { this->packetInfo.offset = packetOffset; this->packetInfo.length = ( XMP_Int32 ) packetLength; this->xmpPacket.assign( this->packetInfo.length, ' ' ); fileRef->Seek( packetOffset, kXMP_SeekFromStart ); fileRef->ReadAll( ( void* )this->xmpPacket.data(), this->packetInfo.length ); FillPacketInfo( this->xmpPacket, &this->packetInfo ); this->containsXMP = true; return; } } this->containsXMP = false; } // SVG_MetaHandler::CacheFileData
void Scanner_MetaHandler::CacheFileData() { XMP_IO* fileRef = this->parent->ioRef; bool beLenient = XMP_OptionIsClear ( this->parent->openFlags, kXMPFiles_OpenStrictly ); int pkt; XMP_Int64 bufPos; size_t bufLen; SXMPMeta * newMeta; XMP_AbortProc abortProc = this->parent->abortProc; void * abortArg = this->parent->abortArg; const bool checkAbort = (abortProc != 0); std::vector<CandidateInfo> candidates; // ! These have SXMPMeta* fields, don't leak on exceptions. this->containsXMP = false; try { // ------------------------------------------------------ // Scan the entire file to find all of the valid packets. XMP_Int64 fileLen = fileRef->Length(); XMPScanner scanner ( fileLen ); enum { kBufferSize = 64*1024 }; XMP_Uns8 buffer [kBufferSize]; fileRef->Rewind(); for ( bufPos = 0; bufPos < fileLen; bufPos += bufLen ) { if ( checkAbort && abortProc(abortArg) ) { XMP_Throw ( "Scanner_MetaHandler::LocateXMP - User abort", kXMPErr_UserAbort ); } bufLen = fileRef->Read ( buffer, kBufferSize ); if ( bufLen == 0 ) XMP_Throw ( "Scanner_MetaHandler::LocateXMP: Read failure", kXMPErr_ExternalFailure ); scanner.Scan ( buffer, bufPos, bufLen ); } // -------------------------------------------------------------- // Parse the valid packet snips, building a vector of candidates. long snipCount = scanner.GetSnipCount(); XMPScanner::SnipInfoVector snips ( snipCount ); scanner.Report ( snips ); for ( pkt = 0; pkt < snipCount; ++pkt ) { if ( checkAbort && abortProc(abortArg) ) { XMP_Throw ( "Scanner_MetaHandler::LocateXMP - User abort", kXMPErr_UserAbort ); } // Seek to the packet then try to parse it. if ( snips[pkt].fState != XMPScanner::eValidPacketSnip ) continue; fileRef->Seek ( snips[pkt].fOffset, kXMP_SeekFromStart ); newMeta = new SXMPMeta(); std::string xmpPacket; xmpPacket.reserve ( (size_t)snips[pkt].fLength ); try { for ( bufPos = 0; bufPos < snips[pkt].fLength; bufPos += bufLen ) { bufLen = kBufferSize; if ( (bufPos + bufLen) > (size_t)snips[pkt].fLength ) bufLen = size_t ( snips[pkt].fLength - bufPos ); (void) fileRef->ReadAll ( buffer, (XMP_Int32)bufLen ); xmpPacket.append ( (const char *)buffer, bufLen ); newMeta->ParseFromBuffer ( (char *)buffer, (XMP_StringLen)bufLen, kXMP_ParseMoreBuffers ); } newMeta->ParseFromBuffer ( 0, 0, kXMP_NoOptions ); } catch ( ... ) { delete newMeta; if ( beLenient ) continue; // Skip if we're being lenient, else rethrow. throw; } // It parsed OK, add it to the array of candidates. candidates.push_back ( CandidateInfo() ); CandidateInfo & newInfo = candidates.back(); newInfo.xmpObj = newMeta; newInfo.xmpPacket.swap ( xmpPacket ); newInfo.packetInfo.offset = snips[pkt].fOffset; newInfo.packetInfo.length = (XMP_Int32)snips[pkt].fLength; newInfo.packetInfo.charForm = snips[pkt].fCharForm; newInfo.packetInfo.writeable = (snips[pkt].fAccess == 'w'); } // ---------------------------------------- // Figure out which packet is the main one. int main = PickMainPacket ( candidates, beLenient ); if ( main != -1 ) { this->packetInfo = candidates[main].packetInfo; this->xmpPacket.swap ( candidates[main].xmpPacket ); this->xmpObj = *candidates[main].xmpObj; this->containsXMP = true; this->processedXMP = true; } for ( pkt = 0; pkt < (int)candidates.size(); ++pkt ) { if ( candidates[pkt].xmpObj != 0 ) delete candidates[pkt].xmpObj; } } catch ( ... ) { // Clean up the SXMPMeta* fields from the vector of candidates. for ( pkt = 0; pkt < (int)candidates.size(); ++pkt ) { if ( candidates[pkt].xmpObj != 0 ) delete candidates[pkt].xmpObj; } throw; } } // Scanner_MetaHandler::CacheFileData
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