// ================================================================================================= // P2_MetaHandler::SetXMPPropertyFromLegacyXML // =========================================== void P2_MetaHandler::SetXMPPropertyFromLegacyXML ( bool /*digestFound*/, XML_NodePtr legacyContext, XMP_StringPtr schemaNS, XMP_StringPtr propName, XMP_StringPtr legacyPropName, bool isLocalized ) { XMP_StringPtr p2NS = this->p2ClipManager.GetManagedClip()->GetP2RootNode()->ns.c_str(); XML_NodePtr legacyProp = legacyContext->GetNamedElement ( p2NS, legacyPropName ); if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { XMP_StringPtr legacyValue = legacyProp->GetLeafContentValue(); if ( ( legacyValue != 0 ) && ( ( *legacyValue != 0 ) || (! this->xmpObj.DoesPropertyExist ( schemaNS, propName )) )) { if ( isLocalized ) { this->xmpObj.SetLocalizedText ( schemaNS, propName, "", "x-default", legacyValue, kXMP_DeleteExisting ); } else { this->xmpObj.SetProperty ( schemaNS, propName, legacyValue, kXMP_DeleteExisting ); } this->containsXMP = true; } } }
void P2_MetaHandler::SetAudioInfoFromLegacyXML ( bool digestFound ) { P2_Clip* p2Clip = this->p2ClipManager.GetManagedClip() ; XMP_StringPtr p2NS = p2Clip->GetP2RootNode()->ns.c_str(); XML_NodePtr legacyAudioContext = p2Clip->GetEssenceListNode(); if ( legacyAudioContext != 0 ) { legacyAudioContext = legacyAudioContext->GetNamedElement ( p2NS, "Audio" ); if ( legacyAudioContext != 0 ) { this->SetXMPPropertyFromLegacyXML ( digestFound, legacyAudioContext, kXMP_NS_DM, "audioSampleRate", "SamplingRate", false ); if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_DM, "audioSampleType" )) ) { XML_NodePtr legacyProp = legacyAudioContext->GetNamedElement ( p2NS, "BitsPerSample" ); if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { const std::string p2BitsPerSample = legacyProp->GetLeafContentValue(); std::string dmSampleType; if ( p2BitsPerSample == "16" ) { dmSampleType = "16Int"; } else if ( p2BitsPerSample == "24" ) { dmSampleType = "32Int"; } if ( ! dmSampleType.empty() ) { this->xmpObj.SetProperty ( kXMP_NS_DM, "audioSampleType", dmSampleType, kXMP_DeleteExisting ); this->containsXMP = true; } } } } } } // P2_MetaHandler::SetAudioInfoFromLegacyXML
void P2_MetaHandler::SetAltitudeFromLegacyXML ( XML_NodePtr legacyLocationContext, bool digestFound ) { if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_EXIF, "GPSAltitude" )) ) { P2_Clip* p2Clip = this->p2ClipManager.GetManagedClip() ; XMP_StringPtr p2NS = p2Clip->GetP2RootNode()->ns.c_str(); XML_NodePtr legacyAltitudeProp = legacyLocationContext->GetNamedElement ( p2NS, "Altitude" ); if ( ( legacyAltitudeProp != 0 ) && legacyAltitudeProp->IsLeafContentNode() ) { this->xmpObj.DeleteProperty ( kXMP_NS_EXIF, "GPSAltitude" ); const std::string legacyGPSValue = legacyAltitudeProp->GetLeafContentValue(); if ( ! legacyGPSValue.empty() ) { int altitude = 0; if ( sscanf ( legacyGPSValue.c_str(), "%d", &altitude ) == 1) { if ( altitude >= 0 ) { // At or above sea level. this->xmpObj.SetProperty ( kXMP_NS_EXIF, "GPSAltitudeRef", "0" ); } else { // Below sea level. altitude = -altitude; this->xmpObj.SetProperty ( kXMP_NS_EXIF, "GPSAltitudeRef", "1" ); } char xmpValue [128]; sprintf ( xmpValue, "%d/1", altitude ); this->xmpObj.SetProperty ( kXMP_NS_EXIF, "GPSAltitude", xmpValue ); this->containsXMP = true; } } } } } // P2_MetaHandler::SetAltitudeFromLegacyXML
void P2_MetaHandler::SetVideoInfoFromLegacyXML ( bool digestFound ) { P2_Clip* p2Clip = this->p2ClipManager.GetManagedClip() ; XMP_StringPtr p2NS = p2Clip->GetP2RootNode()->ns.c_str(); XML_NodePtr legacyVideoContext = p2Clip->GetEssenceListNode(); if ( legacyVideoContext != 0 ) { legacyVideoContext = legacyVideoContext->GetNamedElement ( p2NS, "Video" ); if ( legacyVideoContext != 0 ) { this->SetVideoFrameInfoFromLegacyXML ( legacyVideoContext, digestFound ); this->SetStartTimecodeFromLegacyXML ( legacyVideoContext, digestFound ); this->SetXMPPropertyFromLegacyXML ( digestFound, legacyVideoContext, kXMP_NS_DM, "videoFrameRate", "FrameRate", false ); } } } // P2_MetaHandler::SetVideoInfoFromLegacyXML
void P2_MetaHandler::SetGPSPropertyFromLegacyXML ( XML_NodePtr legacyLocationContext, bool digestFound, XMP_StringPtr propName, XMP_StringPtr legacyPropName ) { if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_EXIF, propName )) ) { P2_Clip* p2Clip = this->p2ClipManager.GetManagedClip() ; XMP_StringPtr p2NS = p2Clip->GetP2RootNode()->ns.c_str(); XML_NodePtr legacyGPSProp = legacyLocationContext->GetNamedElement ( p2NS, legacyPropName ); if ( ( legacyGPSProp != 0 ) && legacyGPSProp->IsLeafContentNode() ) { this->xmpObj.DeleteProperty ( kXMP_NS_EXIF, propName ); const std::string legacyGPSValue = legacyGPSProp->GetLeafContentValue(); if ( ! legacyGPSValue.empty() ) { // Convert from decimal to sexagesimal GPS coordinates char direction = '\0'; double degrees = 0.0; const int numFieldsRead = sscanf ( legacyGPSValue.c_str(), "%c%lf", &direction, °rees ); if ( numFieldsRead == 2 ) { double wholeDegrees = 0.0; const double fractionalDegrees = modf ( degrees, &wholeDegrees ); const double minutes = fractionalDegrees * 60.0; char xmpValue [128]; sprintf ( xmpValue, "%d,%.5lf%c", static_cast<int>(wholeDegrees), minutes, direction ); this->xmpObj.SetProperty ( kXMP_NS_EXIF, propName, xmpValue ); this->containsXMP = true; } } } } } // P2_MetaHandler::SetGPSPropertyFromLegacyXML
void P2_MetaHandler::SetStartTimecodeFromLegacyXML ( XML_NodePtr legacyVideoContext, bool digestFound ) { // Translate start timecode to the format specified by the dynamic media schema. if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_DM, "startTimecode" )) ) { P2_Clip* p2Clip = this->p2ClipManager.GetManagedClip() ; XMP_StringPtr p2NS = p2Clip->GetP2RootNode()->ns.c_str(); XML_NodePtr legacyProp = legacyVideoContext->GetNamedElement ( p2NS, "StartTimecode" ); if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { std::string p2StartTimecode = legacyProp->GetLeafContentValue(); legacyProp = legacyVideoContext->GetNamedElement ( p2NS, "FrameRate" ); if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { const std::string p2FrameRate = legacyProp->GetLeafContentValue(); XMP_StringPtr p2DropFrameFlag = legacyProp->GetAttrValue ( "DropFrameFlag" ); if ( p2DropFrameFlag == 0 ) p2DropFrameFlag = ""; // Make tests easier. std::string dmTimeFormat; if ( ( p2FrameRate == "50i" ) || ( p2FrameRate == "25p" ) ) { dmTimeFormat = "25Timecode"; } else if ( p2FrameRate == "23.98p" ) { dmTimeFormat = "23976Timecode"; } else if ( p2FrameRate == "50p" ) { dmTimeFormat = "50Timecode"; this->AdjustTimeCode( p2StartTimecode, false ); } else if ( ( p2FrameRate == "59.94p" ) && ( p2DropFrameFlag != 0 ) ) { if ( XMP_LitMatch ( p2DropFrameFlag, "true" ) ) { dmTimeFormat = "5994DropTimecode"; } else if ( XMP_LitMatch ( p2DropFrameFlag, "false" ) ) { dmTimeFormat = "5994NonDropTimecode"; } this->AdjustTimeCode( p2StartTimecode, false ); } else if ( (p2FrameRate == "59.94i") || (p2FrameRate == "29.97p") ) { if ( p2DropFrameFlag != 0 ) { if ( XMP_LitMatch ( p2DropFrameFlag, "false" ) ) { dmTimeFormat = "2997NonDropTimecode"; } else if ( XMP_LitMatch ( p2DropFrameFlag, "true" ) ) { // Drop frame NTSC timecode uses semicolons instead of colons as separators. std::string::iterator currCharIt = p2StartTimecode.begin(); const std::string::iterator charsEndIt = p2StartTimecode.end(); for ( ; currCharIt != charsEndIt; ++currCharIt ) { if ( *currCharIt == ':' ) *currCharIt = ';'; } dmTimeFormat = "2997DropTimecode"; } } } if ( ( ! p2StartTimecode.empty() ) && ( ! dmTimeFormat.empty() ) ) { this->xmpObj.SetStructField ( kXMP_NS_DM, "startTimecode", kXMP_NS_DM, "timeValue", p2StartTimecode, 0 ); this->xmpObj.SetStructField ( kXMP_NS_DM, "startTimecode", kXMP_NS_DM, "timeFormat", dmTimeFormat, 0 ); this->containsXMP = true; } } } } } // P2_MetaHandler::SetStartTimecodeFromLegacyXML
void P2_MetaHandler::SetVideoFrameInfoFromLegacyXML ( XML_NodePtr legacyVideoContext, bool digestFound ) { // Map the P2 Codec field to various dynamic media schema fields. if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_DM, "videoFrameSize" )) ) { P2_Clip* p2Clip = this->p2ClipManager.GetManagedClip() ; XMP_StringPtr p2NS = p2Clip->GetP2RootNode()->ns.c_str(); XML_NodePtr legacyProp = legacyVideoContext->GetNamedElement ( p2NS, "Codec" ); if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { const std::string p2Codec = legacyProp->GetLeafContentValue(); std::string dmPixelAspectRatio, dmVideoCompressor, dmWidth, dmHeight; if ( p2Codec == "DV25_411" ) { dmWidth = "720"; dmVideoCompressor = "DV25 4:1:1"; } else if ( p2Codec == "DV25_420" ) { dmWidth = "720"; dmVideoCompressor = "DV25 4:2:0"; } else if ( p2Codec == "DV50_422" ) { dmWidth = "720"; dmVideoCompressor = "DV50 4:2:2"; } else if ( ( p2Codec == "DV100_1080/59.94i" ) || ( p2Codec == "DV100_1080/50i" ) ) { dmVideoCompressor = "DV100"; dmHeight = "1080"; if ( p2Codec == "DV100_1080/59.94i" ) { dmWidth = "1280"; dmPixelAspectRatio = "3/2"; } else { dmWidth = "1440"; dmPixelAspectRatio = "1920/1440"; } } else if ( ( p2Codec == "DV100_720/59.94p" ) || ( p2Codec == "DV100_720/50p" ) ) { dmVideoCompressor = "DV100"; dmHeight = "720"; dmWidth = "960"; dmPixelAspectRatio = "1920/1440"; } else if ( ( p2Codec.compare ( 0, 6, "AVC-I_" ) == 0 ) ) { // This is AVC-Intra footage. The framerate and PAR depend on the "class" attribute in the P2 XML. const XMP_StringPtr codecClass = legacyProp->GetAttrValue( "Class" ); if ( codecClass != 0 ) dmVideoCompressor = "AVC-Intra"; // initializing with default value if ( XMP_LitMatch ( codecClass, "100" ) ) { dmVideoCompressor = "AVC-Intra 100"; dmPixelAspectRatio = "1/1"; if ( p2Codec.compare ( 6, 4, "1080" ) == 0 ) { dmHeight = "1080"; dmWidth = "1920"; } else if ( p2Codec.compare ( 6, 3, "720" ) == 0 ) { dmHeight = "720"; dmWidth = "1280"; } } else if ( XMP_LitMatch ( codecClass, "50" ) ) { dmVideoCompressor = "AVC-Intra 50"; dmPixelAspectRatio = "1920/1440"; if ( p2Codec.compare ( 6, 4, "1080" ) == 0 ) { dmHeight = "1080"; dmWidth = "1440"; } else if ( p2Codec.compare ( 6, 3, "720" ) == 0 ) { dmHeight = "720"; dmWidth = "960"; } } else { // Unknown codec class -- we don't have enough info to determine the // codec, PAR, or aspect ratio dmVideoCompressor = "AVC-Intra"; } } if ( dmWidth == "720" ) { // This is SD footage -- calculate the frame height and pixel aspect ratio using the legacy P2 // FrameRate and AspectRatio fields. legacyProp = legacyVideoContext->GetNamedElement ( p2NS, "FrameRate" ); if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { const std::string p2FrameRate = legacyProp->GetLeafContentValue(); legacyProp = legacyVideoContext->GetNamedElement ( p2NS, "AspectRatio" ); if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { const std::string p2AspectRatio = legacyProp->GetLeafContentValue(); if ( p2FrameRate == "50i" ) { // Standard Definition PAL. dmHeight = "576"; if ( p2AspectRatio == "4:3" ) { dmPixelAspectRatio = "768/702"; } else if ( p2AspectRatio == "16:9" ) { dmPixelAspectRatio = "1024/702"; } } else if ( p2FrameRate == "59.94i" ) { // Standard Definition NTSC. dmHeight = "480"; if ( p2AspectRatio == "4:3" ) { dmPixelAspectRatio = "10/11"; } else if ( p2AspectRatio == "16:9" ) { dmPixelAspectRatio = "40/33"; } } } } } if ( ! dmPixelAspectRatio.empty() ) { this->xmpObj.SetProperty ( kXMP_NS_DM, "videoPixelAspectRatio", dmPixelAspectRatio, kXMP_DeleteExisting ); this->containsXMP = true; } if ( ! dmVideoCompressor.empty() ) { this->xmpObj.SetProperty ( kXMP_NS_DM, "videoCompressor", dmVideoCompressor, kXMP_DeleteExisting ); this->containsXMP = true; } if ( ( ! dmWidth.empty() ) && ( ! dmHeight.empty() ) ) { this->xmpObj.SetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_XMP_Dimensions, "w", dmWidth, 0 ); this->xmpObj.SetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_XMP_Dimensions, "h", dmHeight, 0 ); this->xmpObj.SetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_XMP_Dimensions, "unit", "pixel", 0 ); this->containsXMP = true; } } } } // P2_MetaHandler::SetVideoFrameInfoFromLegacyXML
void P2_MetaHandler::UpdateFile ( bool doSafeUpdate ) { if ( ! this->needsUpdate ) return; this->needsUpdate = false; // Make sure only called once. XMP_Assert ( this->parent->UsesLocalIO() ); // Update the internal legacy XML tree if we have one, and set the digest in the XMP. bool updateLegacyXML = false; P2_Clip* p2Clip = 0; XML_NodePtr clipMetadata = 0; if ( this->p2ClipManager.IsValidP2() ) { p2Clip=this->p2ClipManager.GetManagedClip(); clipMetadata = p2Clip->GetClipMetadataNode(); if ( clipMetadata != 0 ) { bool xmpFound; std::string xmpValue; XML_Node * xmlNode; xmpFound = this->xmpObj.GetLocalizedText ( kXMP_NS_DC, "title", "", "x-default", 0, &xmpValue, 0 ); if ( xmpFound && p2Clip->GetClipContentNode()) { xmlNode = this->ForceChildElement ( p2Clip->GetClipContentNode(), "ClipName", 3, false ); if ( xmpValue != xmlNode->GetLeafContentValue() ) { xmlNode->SetLeafContentValue ( xmpValue.c_str() ); updateLegacyXML = true; } } xmpFound = this->xmpObj.GetArrayItem ( kXMP_NS_DC, "creator", 1, &xmpValue, 0 ); if ( xmpFound ) { xmlNode = this->ForceChildElement ( clipMetadata , "Access", 3, false ); // "Creator" must be first child of "Access" node else Panasonic P2 Viewer gives an error. xmlNode = this->ForceChildElement ( xmlNode, "Creator", 4 , true); if ( xmpValue != xmlNode->GetLeafContentValue() ) { xmlNode->SetLeafContentValue ( xmpValue.c_str() ); updateLegacyXML = true; } } } // Half the startTimeCode frame number value in XML if require so std::string xmpStartTimeCode; bool isTimecodeExists = this->xmpObj.GetStructField(kXMP_NS_DM, "startTimecode", kXMP_NS_DM, "timeValue", &xmpStartTimeCode, 0); if (isTimecodeExists) { std::string frameFormat; this->xmpObj.GetStructField(kXMP_NS_DM, "startTimecode", kXMP_NS_DM, "timeFormat", &frameFormat, 0); if (frameFormat == "50Timecode" || frameFormat == "5994DropTimecode" || frameFormat == "5994NonDropTimecode") { p2Clip = this->p2ClipManager.GetManagedClip(); XMP_StringPtr p2NS = p2Clip->GetP2RootNode()->ns.c_str(); XML_NodePtr legacyVideoContext = p2Clip->GetEssenceListNode(); if (legacyVideoContext != 0) { legacyVideoContext = legacyVideoContext->GetNamedElement(p2NS, "Video"); XML_NodePtr legacyProp = legacyVideoContext->GetNamedElement(p2NS, "StartTimecode"); if ((legacyProp != 0) && legacyProp->IsLeafContentNode()) { AdjustTimeCode( xmpStartTimeCode, true ); if (xmpStartTimeCode != legacyProp->GetLeafContentValue()) { legacyProp->SetLeafContentValue(xmpStartTimeCode.c_str()); updateLegacyXML = true; } } } } } std::string newDigest; this->p2ClipManager.GetManagedClip()->CreateDigest ( &newDigest ); this->xmpObj.SetStructField ( kXMP_NS_XMP, "NativeDigests", kXMP_NS_XMP, "P2", newDigest.c_str(), kXMP_DeleteExisting ); } this->xmpObj.SerializeToBuffer ( &this->xmpPacket, this->GetSerializeOptions() ); // ----------------------------------------------------------------------- // Update the XMP file first, don't let legacy XML failures block the XMP. std::string xmpPath; this->MakeClipFilePath ( &xmpPath, ".XMP" ); bool haveXMP = Host_IO::Exists ( xmpPath.c_str() ); if ( ! haveXMP ) { XMP_Assert ( this->parent->ioRef == 0 ); Host_IO::Create ( xmpPath.c_str() ); this->parent->ioRef = XMPFiles_IO::New_XMPFiles_IO ( xmpPath.c_str(), Host_IO::openReadWrite ); if ( this->parent->ioRef == 0 ) XMP_Throw ( "Failure opening P2 XMP file", kXMPErr_ExternalFailure ); } XMP_IO* xmpFile = this->parent->ioRef; XMP_Assert ( xmpFile != 0 ); XIO::ReplaceTextFile ( xmpFile, this->xmpPacket, (haveXMP & doSafeUpdate) ); // -------------------------------------------- // Now update the legacy XML file if necessary. if ( updateLegacyXML ) { std::string legacyXML, xmlPath; /*bug # 3217688: xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance namespace must be defined at the root node "P2Main" in legacy XML else Panasonic P2 Viewer gives an error. So we are adding a dummy attribute with this namespace to clipContent/clipMetadata (whichever is non-null) before serializing the XML tree. We are also undoing it below after serialization.*/ XML_Node *parentNode = AddXSINamespace(p2Clip->GetClipContentNode(), clipMetadata); p2Clip->SerializeP2ClipContent ( legacyXML ); if(parentNode){ // Remove the dummy attribute added to clipContent/clipMetadata. delete parentNode->attrs[parentNode->attrs.size()-1]; parentNode->attrs.pop_back(); } this->MakeClipFilePath ( &xmlPath, ".XML" ); bool haveXML = Host_IO::Exists ( xmlPath.c_str() ); if ( ! haveXML ) Host_IO::Create ( xmlPath.c_str() ); Host_IO::FileRef hostRef = Host_IO::Open ( xmlPath.c_str(), Host_IO::openReadWrite ); if ( hostRef == Host_IO::noFileRef ) XMP_Throw ( "Failure opening P2 legacy XML file", kXMPErr_ExternalFailure ); XMPFiles_IO origXML ( hostRef, xmlPath.c_str(), Host_IO::openReadWrite ); XIO::ReplaceTextFile ( &origXML, legacyXML, (haveXML & doSafeUpdate) ); origXML.Close(); } } // P2_MetaHandler::UpdateFile
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