You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1213 lines
39 KiB
C++
1213 lines
39 KiB
C++
// =================================================================================================
|
|
// Copyright 2002-2008 Adobe Systems Incorporated
|
|
// All Rights Reserved.
|
|
//
|
|
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
|
|
// of the Adobe license agreement accompanying it.
|
|
//
|
|
// Adobe patent application tracking #P435, entitled 'Unique markers to simplify embedding data of
|
|
// one format in a file with a different format', inventors: Sean Parent, Greg Gilley.
|
|
// =================================================================================================
|
|
|
|
#include "XMP_Environment.h" // ! This must be the first include!
|
|
#include "XMPCore_Impl.hpp"
|
|
|
|
#include "XMPMeta.hpp"
|
|
#include "XMPIterator.hpp"
|
|
#include "XMPUtils.hpp"
|
|
|
|
#include "XMP_Version.h"
|
|
#include "UnicodeInlines.incl_cpp"
|
|
#include "UnicodeConversions.hpp"
|
|
#include "ExpatAdapter.hpp"
|
|
|
|
#if XMP_DebugBuild
|
|
#include <iostream>
|
|
#endif
|
|
|
|
using namespace std;
|
|
|
|
#if XMP_WinBuild
|
|
#ifdef _MSC_VER
|
|
#pragma warning ( disable : 4533 ) // initialization of '...' is skipped by 'goto ...'
|
|
#pragma warning ( disable : 4702 ) // unreachable code
|
|
#pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning)
|
|
#endif
|
|
#endif
|
|
|
|
|
|
// *** Use the XMP_PropIsXyz (Schema, Simple, Struct, Array, ...) macros
|
|
// *** Add debug codegen checks, e.g. that typical masking operations really work
|
|
// *** Change all uses of strcmp and strncmp to XMP_LitMatch and XMP_LitNMatch
|
|
|
|
|
|
// =================================================================================================
|
|
// Local Types and Constants
|
|
// =========================
|
|
|
|
typedef unsigned char XMP_CLTMatch;
|
|
|
|
enum { // Values for XMP_CLTMatch.
|
|
kXMP_CLT_NoValues,
|
|
kXMP_CLT_SpecificMatch,
|
|
kXMP_CLT_SingleGeneric,
|
|
kXMP_CLT_MultipleGeneric,
|
|
kXMP_CLT_XDefault,
|
|
kXMP_CLT_FirstItem
|
|
};
|
|
|
|
|
|
// =================================================================================================
|
|
// Static Variables
|
|
// ================
|
|
|
|
|
|
// =================================================================================================
|
|
// Local Utilities
|
|
// ===============
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// SetNodeValue
|
|
// ------------
|
|
|
|
static inline void
|
|
SetNodeValue ( XMP_Node * node, XMP_StringPtr value )
|
|
{
|
|
|
|
#if XMP_DebugBuild // ! Hack to force an assert.
|
|
if ( (node->name == "xmp:TestAssertNotify") && XMP_LitMatch ( value, "DoIt!" ) ) {
|
|
XMP_Assert ( node->name != "xmp:TestAssertNotify" );
|
|
}
|
|
#endif
|
|
|
|
node->value = value;
|
|
|
|
XMP_Uns8* chPtr = (XMP_Uns8*) node->value.c_str(); // Check for valid UTF-8, replace ASCII controls with a space.
|
|
while ( *chPtr != 0 ) {
|
|
while ( (*chPtr != 0) && (*chPtr < 0x80) ) {
|
|
if ( *chPtr < 0x20 ) {
|
|
if ( (*chPtr != kTab) && (*chPtr != kLF) && (*chPtr != kCR) ) *chPtr = 0x20;
|
|
} else if (*chPtr == 0x7F ) {
|
|
*chPtr = 0x20;
|
|
}
|
|
++chPtr;
|
|
}
|
|
XMP_Assert ( (*chPtr == 0) || (*chPtr >= 0x80) );
|
|
if ( *chPtr != 0 ) (void) GetCodePoint ( (const XMP_Uns8 **) &chPtr ); // Throws for bad UTF-8.
|
|
}
|
|
|
|
if ( XMP_PropIsQualifier(node->options) && (node->name == "xml:lang") ) NormalizeLangValue ( &node->value );
|
|
|
|
#if 0 // *** XMP_DebugBuild
|
|
node->_valuePtr = node->value.c_str();
|
|
#endif
|
|
|
|
} // SetNodeValue
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// SetNode
|
|
// -------
|
|
//
|
|
// The internals for SetProperty and related calls, used after the node is found or created.
|
|
|
|
static void
|
|
SetNode ( XMP_Node * node, XMP_StringPtr value, XMP_OptionBits options )
|
|
{
|
|
if ( options & kXMP_DeleteExisting ) {
|
|
XMP_ClearOption ( options, kXMP_DeleteExisting );
|
|
node->options = options;
|
|
node->value.erase();
|
|
node->RemoveChildren();
|
|
node->RemoveQualifiers();
|
|
}
|
|
|
|
node->options |= options; // Keep options set by FindNode when creating a new node.
|
|
|
|
if ( value != 0 ) {
|
|
|
|
// This is setting the value of a leaf node.
|
|
if ( node->options & kXMP_PropCompositeMask ) XMP_Throw ( "Composite nodes can't have values", kXMPErr_BadXPath );
|
|
XMP_Assert ( node->children.empty() );
|
|
SetNodeValue ( node, value );
|
|
|
|
} else {
|
|
|
|
// This is setting up an array or struct.
|
|
if ( ! node->value.empty() ) XMP_Throw ( "Composite nodes can't have values", kXMPErr_BadXPath );
|
|
if ( node->options & kXMP_PropCompositeMask ) { // Can't change an array to a struct, or vice versa.
|
|
if ( (options & kXMP_PropCompositeMask) != (node->options & kXMP_PropCompositeMask) ) {
|
|
XMP_Throw ( "Requested and existing composite form mismatch", kXMPErr_BadXPath );
|
|
}
|
|
}
|
|
node->RemoveChildren();
|
|
|
|
}
|
|
|
|
} // SetNode
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// DoSetArrayItem
|
|
// --------------
|
|
|
|
static void
|
|
DoSetArrayItem ( XMP_Node * arrayNode,
|
|
XMP_Index itemIndex,
|
|
XMP_StringPtr itemValue,
|
|
XMP_OptionBits options )
|
|
{
|
|
XMP_OptionBits itemLoc = options & kXMP_PropArrayLocationMask;
|
|
XMP_Index arraySize = arrayNode->children.size();
|
|
|
|
options &= ~kXMP_PropArrayLocationMask;
|
|
options = VerifySetOptions ( options, itemValue );
|
|
|
|
// Now locate or create the item node and set the value. Note the index parameter is one-based!
|
|
// The index can be in the range [0..size+1] or "last", normalize it and check the insert flags.
|
|
// The order of the normalization checks is important. If the array is empty we end up with an
|
|
// index and location to set item size+1.
|
|
|
|
XMP_Node * itemNode = 0;
|
|
|
|
if ( itemIndex == kXMP_ArrayLastItem ) itemIndex = arraySize;
|
|
if ( (itemIndex == 0) && (itemLoc == kXMP_InsertAfterItem) ) {
|
|
itemIndex = 1;
|
|
itemLoc = kXMP_InsertBeforeItem;
|
|
}
|
|
if ( (itemIndex == arraySize) && (itemLoc == kXMP_InsertAfterItem) ) {
|
|
itemIndex += 1;
|
|
itemLoc = 0;
|
|
}
|
|
if ( (itemIndex == arraySize+1) && (itemLoc == kXMP_InsertBeforeItem) ) itemLoc = 0;
|
|
|
|
if ( itemIndex == arraySize+1 ) {
|
|
|
|
if ( itemLoc != 0 ) XMP_Throw ( "Can't insert before or after implicit new item", kXMPErr_BadIndex );
|
|
itemNode = new XMP_Node ( arrayNode, kXMP_ArrayItemName, 0 );
|
|
arrayNode->children.push_back ( itemNode );
|
|
|
|
} else {
|
|
|
|
if ( (itemIndex < 1) || (itemIndex > arraySize) ) XMP_Throw ( "Array index out of bounds", kXMPErr_BadIndex );
|
|
--itemIndex; // ! Convert the index to a C zero-based value!
|
|
if ( itemLoc == 0 ) {
|
|
itemNode = arrayNode->children[itemIndex];
|
|
} else {
|
|
XMP_NodePtrPos itemPos = arrayNode->children.begin() + itemIndex;
|
|
if ( itemLoc == kXMP_InsertAfterItem ) ++itemPos;
|
|
itemNode = new XMP_Node ( arrayNode, kXMP_ArrayItemName, 0 );
|
|
itemPos = arrayNode->children.insert ( itemPos, itemNode );
|
|
}
|
|
|
|
}
|
|
|
|
SetNode ( itemNode, itemValue, options );
|
|
|
|
} // DoSetArrayItem
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// ChooseLocalizedText
|
|
// -------------------
|
|
//
|
|
// 1. Look for an exact match with the specific language.
|
|
// 2. If a generic language is given, look for partial matches.
|
|
// 3. Look for an "x-default" item.
|
|
// 4. Choose the first item.
|
|
|
|
static XMP_CLTMatch
|
|
ChooseLocalizedText ( const XMP_Node * arrayNode,
|
|
XMP_StringPtr genericLang,
|
|
XMP_StringPtr specificLang,
|
|
const XMP_Node * * itemNode )
|
|
{
|
|
const XMP_Node * currItem = 0;
|
|
const size_t itemLim = arrayNode->children.size();
|
|
size_t itemNum;
|
|
|
|
// See if the array has the right form. Allow empty alt arrays, that is what parsing returns.
|
|
// *** Should check alt-text bit when that is reliably maintained.
|
|
|
|
if ( ! ( XMP_ArrayIsAltText(arrayNode->options) ||
|
|
(arrayNode->children.empty() && XMP_ArrayIsAlternate(arrayNode->options)) ) ) {
|
|
XMP_Throw ( "Localized text array is not alt-text", kXMPErr_BadXPath );
|
|
}
|
|
if ( arrayNode->children.empty() ) {
|
|
*itemNode = 0;
|
|
return kXMP_CLT_NoValues;
|
|
}
|
|
|
|
for ( itemNum = 0; itemNum < itemLim; ++itemNum ) {
|
|
currItem = arrayNode->children[itemNum];
|
|
if ( currItem->options & kXMP_PropCompositeMask ) {
|
|
XMP_Throw ( "Alt-text array item is not simple", kXMPErr_BadXPath );
|
|
}
|
|
if ( currItem->qualifiers.empty() || (currItem->qualifiers[0]->name != "xml:lang") ) {
|
|
XMP_Throw ( "Alt-text array item has no language qualifier", kXMPErr_BadXPath );
|
|
}
|
|
}
|
|
|
|
// Look for an exact match with the specific language.
|
|
for ( itemNum = 0; itemNum < itemLim; ++itemNum ) {
|
|
currItem = arrayNode->children[itemNum];
|
|
if ( currItem->qualifiers[0]->value == specificLang ) {
|
|
*itemNode = currItem;
|
|
return kXMP_CLT_SpecificMatch;
|
|
}
|
|
}
|
|
|
|
if ( *genericLang != 0 ) {
|
|
|
|
// Look for the first partial match with the generic language.
|
|
const size_t genericLen = strlen ( genericLang );
|
|
for ( itemNum = 0; itemNum < itemLim; ++itemNum ) {
|
|
currItem = arrayNode->children[itemNum];
|
|
XMP_StringPtr currLang = currItem->qualifiers[0]->value.c_str();
|
|
const size_t currLangSize = currItem->qualifiers[0]->value.size();
|
|
if ( (currLangSize >= genericLen) &&
|
|
XMP_LitNMatch ( currLang, genericLang, genericLen ) &&
|
|
((currLangSize == genericLen) || (currLang[genericLen] == '-')) ) {
|
|
*itemNode = currItem;
|
|
break; // ! Don't return, need to look for other matches.
|
|
}
|
|
}
|
|
|
|
if ( itemNum < itemLim ) {
|
|
|
|
// Look for a second partial match with the generic language.
|
|
for ( ++itemNum; itemNum < itemLim; ++itemNum ) {
|
|
currItem = arrayNode->children[itemNum];
|
|
XMP_StringPtr currLang = currItem->qualifiers[0]->value.c_str();
|
|
const size_t currLangSize = currItem->qualifiers[0]->value.size();
|
|
if ( (currLangSize >= genericLen) &&
|
|
XMP_LitNMatch ( currLang, genericLang, genericLen ) &&
|
|
((currLangSize == genericLen) || (currLang[genericLen] == '-')) ) {
|
|
return kXMP_CLT_MultipleGeneric; // ! Leave itemNode with the first partial match.
|
|
}
|
|
}
|
|
return kXMP_CLT_SingleGeneric; // No second partial match was found.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Look for an 'x-default' item.
|
|
for ( itemNum = 0; itemNum < itemLim; ++itemNum ) {
|
|
currItem = arrayNode->children[itemNum];
|
|
if ( currItem->qualifiers[0]->value == "x-default" ) {
|
|
*itemNode = currItem;
|
|
return kXMP_CLT_XDefault;
|
|
}
|
|
}
|
|
|
|
// Everything failed, choose the first item.
|
|
*itemNode = arrayNode->children[0];
|
|
return kXMP_CLT_FirstItem;
|
|
|
|
} // ChooseLocalizedText
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// AppendLangItem
|
|
// --------------
|
|
|
|
static void
|
|
AppendLangItem ( XMP_Node * arrayNode, XMP_StringPtr itemLang, XMP_StringPtr itemValue )
|
|
{
|
|
XMP_Node * newItem = new XMP_Node ( arrayNode, kXMP_ArrayItemName, itemValue, (kXMP_PropHasQualifiers | kXMP_PropHasLang) );
|
|
XMP_Node * langQual = new XMP_Node ( newItem, "xml:lang", itemLang, kXMP_PropIsQualifier );
|
|
newItem->qualifiers.push_back ( langQual );
|
|
|
|
if ( (arrayNode->children.empty()) || (langQual->value != "x-default") ) {
|
|
arrayNode->children.push_back ( newItem );
|
|
} else {
|
|
arrayNode->children.insert ( arrayNode->children.begin(), newItem );
|
|
}
|
|
|
|
} // AppendLangItem
|
|
|
|
|
|
// =================================================================================================
|
|
// Class Methods
|
|
// =============
|
|
//
|
|
//
|
|
// =================================================================================================
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// GetProperty
|
|
// -----------
|
|
|
|
bool
|
|
XMPMeta::GetProperty ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr propName,
|
|
XMP_StringPtr * propValue,
|
|
XMP_StringLen * valueSize,
|
|
XMP_OptionBits * options ) const
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
|
|
XMP_Assert ( (propValue != 0) && (valueSize != 0) && (options != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_ExpandedXPath expPath;
|
|
ExpandXPath ( schemaNS, propName, &expPath );
|
|
|
|
XMP_Node * propNode = FindConstNode ( &tree, expPath );
|
|
if ( propNode == 0 ) return false;
|
|
|
|
*propValue = propNode->value.c_str();
|
|
*valueSize = propNode->value.size();
|
|
*options = propNode->options;
|
|
|
|
return true;
|
|
|
|
} // GetProperty
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// GetArrayItem
|
|
// ------------
|
|
|
|
bool
|
|
XMPMeta::GetArrayItem ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr arrayName,
|
|
XMP_Index itemIndex,
|
|
XMP_StringPtr * itemValue,
|
|
XMP_StringLen * valueSize,
|
|
XMP_OptionBits * options ) const
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (arrayName != 0) ); // Enforced by wrapper.
|
|
XMP_Assert ( (itemValue != 0) && (valueSize != 0) && (options != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_StringPtr itemPath;
|
|
XMP_StringLen pathLen;
|
|
|
|
XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &itemPath, &pathLen );
|
|
return GetProperty ( schemaNS, itemPath, itemValue, valueSize, options );
|
|
|
|
} // GetArrayItem
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// GetStructField
|
|
// --------------
|
|
|
|
bool
|
|
XMPMeta::GetStructField ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr structName,
|
|
XMP_StringPtr fieldNS,
|
|
XMP_StringPtr fieldName,
|
|
XMP_StringPtr * fieldValue,
|
|
XMP_StringLen * valueSize,
|
|
XMP_OptionBits * options ) const
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (structName != 0) && (fieldNS != 0) && (fieldName != 0) ); // Enforced by wrapper.
|
|
XMP_Assert ( (fieldValue != 0) && (valueSize != 0) && (options != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_StringPtr fieldPath;
|
|
XMP_StringLen pathLen;
|
|
|
|
XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath, &pathLen );
|
|
return GetProperty ( schemaNS, fieldPath, fieldValue, valueSize, options );
|
|
|
|
} // GetStructField
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// GetQualifier
|
|
// ------------
|
|
|
|
bool
|
|
XMPMeta::GetQualifier ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr propName,
|
|
XMP_StringPtr qualNS,
|
|
XMP_StringPtr qualName,
|
|
XMP_StringPtr * qualValue,
|
|
XMP_StringLen * valueSize,
|
|
XMP_OptionBits * options ) const
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (propName != 0) && (qualNS != 0) && (qualName != 0) ); // Enforced by wrapper.
|
|
XMP_Assert ( (qualValue != 0) && (valueSize != 0) && (options != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_StringPtr qualPath;
|
|
XMP_StringLen pathLen;
|
|
|
|
XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath, &pathLen );
|
|
return GetProperty ( schemaNS, qualPath, qualValue, valueSize, options );
|
|
|
|
} // GetQualifier
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// SetProperty
|
|
// -----------
|
|
|
|
// *** Should handle array items specially, calling SetArrayItem.
|
|
|
|
void
|
|
XMPMeta::SetProperty ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr propName,
|
|
XMP_StringPtr propValue,
|
|
XMP_OptionBits options )
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
|
|
|
|
options = VerifySetOptions ( options, propValue );
|
|
|
|
XMP_ExpandedXPath expPath;
|
|
ExpandXPath ( schemaNS, propName, &expPath );
|
|
|
|
XMP_Node * propNode = FindNode ( &tree, expPath, kXMP_CreateNodes, options );
|
|
if ( propNode == 0 ) XMP_Throw ( "Specified property does not exist", kXMPErr_BadXPath );
|
|
|
|
SetNode ( propNode, propValue, options );
|
|
|
|
} // SetProperty
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// SetArrayItem
|
|
// ------------
|
|
|
|
void
|
|
XMPMeta::SetArrayItem ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr arrayName,
|
|
XMP_Index itemIndex,
|
|
XMP_StringPtr itemValue,
|
|
XMP_OptionBits options )
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (arrayName != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_ExpandedXPath arrayPath;
|
|
ExpandXPath ( schemaNS, arrayName, &arrayPath );
|
|
XMP_Node * arrayNode = FindNode ( &tree, arrayPath, kXMP_ExistingOnly ); // Just lookup, don't try to create.
|
|
if ( arrayNode == 0 ) XMP_Throw ( "Specified array does not exist", kXMPErr_BadXPath );
|
|
|
|
DoSetArrayItem ( arrayNode, itemIndex, itemValue, options );
|
|
|
|
} // SetArrayItem
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// AppendArrayItem
|
|
// ---------------
|
|
|
|
void
|
|
XMPMeta::AppendArrayItem ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr arrayName,
|
|
XMP_OptionBits arrayOptions,
|
|
XMP_StringPtr itemValue,
|
|
XMP_OptionBits options )
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (arrayName != 0) ); // Enforced by wrapper.
|
|
|
|
arrayOptions = VerifySetOptions ( arrayOptions, 0 );
|
|
if ( (arrayOptions & ~kXMP_PropArrayFormMask) != 0 ) {
|
|
XMP_Throw ( "Only array form flags allowed for arrayOptions", kXMPErr_BadOptions );
|
|
}
|
|
|
|
// Locate or create the array. If it already exists, make sure the array form from the options
|
|
// parameter is compatible with the current state.
|
|
|
|
XMP_ExpandedXPath arrayPath;
|
|
ExpandXPath ( schemaNS, arrayName, &arrayPath );
|
|
XMP_Node * arrayNode = FindNode ( &tree, arrayPath, kXMP_ExistingOnly ); // Just lookup, don't try to create.
|
|
|
|
if ( arrayNode != 0 ) {
|
|
// The array exists, make sure the form is compatible. Zero arrayForm means take what exists.
|
|
if ( ! (arrayNode->options & kXMP_PropValueIsArray) ) {
|
|
XMP_Throw ( "The named property is not an array", kXMPErr_BadXPath );
|
|
}
|
|
#if 0
|
|
// *** Disable for now. Need to do some general rethinking of semantic checks.
|
|
if ( (arrayOptions != 0) && (arrayOptions != (arrayNode->options & kXMP_PropArrayFormMask)) ) {
|
|
XMP_Throw ( "Mismatch of existing and specified array form", kXMPErr_BadOptions );
|
|
}
|
|
#endif
|
|
} else {
|
|
// The array does not exist, try to create it.
|
|
if ( arrayOptions == 0 ) XMP_Throw ( "Explicit arrayOptions required to create new array", kXMPErr_BadOptions );
|
|
arrayNode = FindNode ( &tree, arrayPath, kXMP_CreateNodes, arrayOptions );
|
|
if ( arrayNode == 0 ) XMP_Throw ( "Failure creating array node", kXMPErr_BadXPath );
|
|
}
|
|
|
|
DoSetArrayItem ( arrayNode, kXMP_ArrayLastItem, itemValue, (options | kXMP_InsertAfterItem) );
|
|
|
|
} // AppendArrayItem
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// SetStructField
|
|
// --------------
|
|
|
|
void
|
|
XMPMeta::SetStructField ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr structName,
|
|
XMP_StringPtr fieldNS,
|
|
XMP_StringPtr fieldName,
|
|
XMP_StringPtr fieldValue,
|
|
XMP_OptionBits options )
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (structName != 0) && (fieldNS != 0) && (fieldName != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_StringPtr fieldPath;
|
|
XMP_StringLen pathLen;
|
|
|
|
XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath, &pathLen );
|
|
SetProperty ( schemaNS, fieldPath, fieldValue, options );
|
|
|
|
} // SetStructField
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// SetQualifier
|
|
// ------------
|
|
|
|
void
|
|
XMPMeta::SetQualifier ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr propName,
|
|
XMP_StringPtr qualNS,
|
|
XMP_StringPtr qualName,
|
|
XMP_StringPtr qualValue,
|
|
XMP_OptionBits options )
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (propName != 0) && (qualNS != 0) && (qualName != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_StringPtr qualPath;
|
|
XMP_StringLen pathLen;
|
|
|
|
XMP_ExpandedXPath expPath;
|
|
ExpandXPath ( schemaNS, propName, &expPath );
|
|
XMP_Node * propNode = FindNode ( &tree, expPath, kXMP_ExistingOnly );
|
|
if ( propNode == 0 ) XMP_Throw ( "Specified property does not exist", kXMPErr_BadXPath );
|
|
|
|
XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath, &pathLen );
|
|
SetProperty ( schemaNS, qualPath, qualValue, options );
|
|
|
|
} // SetQualifier
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// DeleteProperty
|
|
// --------------
|
|
|
|
void
|
|
XMPMeta::DeleteProperty ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr propName )
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_ExpandedXPath expPath;
|
|
ExpandXPath ( schemaNS, propName, &expPath );
|
|
|
|
XMP_NodePtrPos ptrPos;
|
|
XMP_Node * propNode = FindNode ( &tree, expPath, kXMP_ExistingOnly, kXMP_NoOptions, &ptrPos );
|
|
if ( propNode == 0 ) return;
|
|
XMP_Node * parentNode = propNode->parent;
|
|
|
|
// Erase the pointer from the parent's vector, then delete the node and all below it.
|
|
|
|
if ( ! (propNode->options & kXMP_PropIsQualifier) ) {
|
|
|
|
parentNode->children.erase ( ptrPos );
|
|
DeleteEmptySchema ( parentNode );
|
|
|
|
} else {
|
|
|
|
if ( propNode->name == "xml:lang" ) {
|
|
XMP_Assert ( parentNode->options & kXMP_PropHasLang ); // *** &= ~flag would be safer
|
|
parentNode->options ^= kXMP_PropHasLang;
|
|
} else if ( propNode->name == "rdf:type" ) {
|
|
XMP_Assert ( parentNode->options & kXMP_PropHasType );
|
|
parentNode->options ^= kXMP_PropHasType;
|
|
}
|
|
|
|
parentNode->qualifiers.erase ( ptrPos );
|
|
XMP_Assert ( parentNode->options & kXMP_PropHasQualifiers );
|
|
if ( parentNode->qualifiers.empty() ) parentNode->options ^= kXMP_PropHasQualifiers;
|
|
|
|
}
|
|
|
|
delete propNode; // ! The destructor takes care of the whole subtree.
|
|
|
|
} // DeleteProperty
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// DeleteArrayItem
|
|
// ---------------
|
|
|
|
void
|
|
XMPMeta::DeleteArrayItem ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr arrayName,
|
|
XMP_Index itemIndex )
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (arrayName != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_StringPtr itemPath;
|
|
XMP_StringLen pathLen;
|
|
|
|
XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &itemPath, &pathLen );
|
|
DeleteProperty ( schemaNS, itemPath );
|
|
|
|
} // DeleteArrayItem
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// DeleteStructField
|
|
// -----------------
|
|
|
|
void
|
|
XMPMeta::DeleteStructField ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr structName,
|
|
XMP_StringPtr fieldNS,
|
|
XMP_StringPtr fieldName )
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (structName != 0) && (fieldNS != 0) && (fieldName != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_StringPtr fieldPath;
|
|
XMP_StringLen pathLen;
|
|
|
|
XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath, &pathLen );
|
|
DeleteProperty ( schemaNS, fieldPath );
|
|
|
|
} // DeleteStructField
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// DeleteQualifier
|
|
// ---------------
|
|
|
|
void
|
|
XMPMeta::DeleteQualifier ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr propName,
|
|
XMP_StringPtr qualNS,
|
|
XMP_StringPtr qualName )
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (propName != 0) && (qualNS != 0) && (qualName != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_StringPtr qualPath;
|
|
XMP_StringLen pathLen;
|
|
|
|
XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath, &pathLen );
|
|
DeleteProperty ( schemaNS, qualPath );
|
|
|
|
} // DeleteQualifier
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// DoesPropertyExist
|
|
// -----------------
|
|
|
|
bool
|
|
XMPMeta::DoesPropertyExist ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr propName ) const
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_ExpandedXPath expPath;
|
|
ExpandXPath ( schemaNS, propName, &expPath );
|
|
|
|
XMP_Node * propNode = FindConstNode ( &tree, expPath );
|
|
return (propNode != 0);
|
|
|
|
} // DoesPropertyExist
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// DoesArrayItemExist
|
|
// ------------------
|
|
|
|
bool
|
|
XMPMeta::DoesArrayItemExist ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr arrayName,
|
|
XMP_Index itemIndex ) const
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (arrayName != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_StringPtr itemPath;
|
|
XMP_StringLen pathLen;
|
|
|
|
XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &itemPath, &pathLen );
|
|
return DoesPropertyExist ( schemaNS, itemPath );
|
|
|
|
} // DoesArrayItemExist
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// DoesStructFieldExist
|
|
// --------------------
|
|
|
|
bool
|
|
XMPMeta::DoesStructFieldExist ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr structName,
|
|
XMP_StringPtr fieldNS,
|
|
XMP_StringPtr fieldName ) const
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (structName != 0) && (fieldNS != 0) && (fieldName != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_StringPtr fieldPath;
|
|
XMP_StringLen pathLen;
|
|
|
|
XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath, &pathLen );
|
|
return DoesPropertyExist ( schemaNS, fieldPath );
|
|
|
|
} // DoesStructFieldExist
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// DoesQualifierExist
|
|
// ------------------
|
|
|
|
bool
|
|
XMPMeta::DoesQualifierExist ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr propName,
|
|
XMP_StringPtr qualNS,
|
|
XMP_StringPtr qualName ) const
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (propName != 0) && (qualNS != 0) && (qualName != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_StringPtr qualPath;
|
|
XMP_StringLen pathLen;
|
|
|
|
XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath, &pathLen );
|
|
return DoesPropertyExist ( schemaNS, qualPath );
|
|
|
|
} // DoesQualifierExist
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// GetLocalizedText
|
|
// ----------------
|
|
|
|
bool
|
|
XMPMeta::GetLocalizedText ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr arrayName,
|
|
XMP_StringPtr _genericLang,
|
|
XMP_StringPtr _specificLang,
|
|
XMP_StringPtr * actualLang,
|
|
XMP_StringLen * langSize,
|
|
XMP_StringPtr * itemValue,
|
|
XMP_StringLen * valueSize,
|
|
XMP_OptionBits * options ) const
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (arrayName != 0) && (_genericLang != 0) && (_specificLang != 0) ); // Enforced by wrapper.
|
|
XMP_Assert ( (actualLang != 0) && (langSize != 0) ); // Enforced by wrapper.
|
|
XMP_Assert ( (itemValue != 0) && (valueSize != 0) && (options != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_VarString zGenericLang ( _genericLang );
|
|
XMP_VarString zSpecificLang ( _specificLang );
|
|
NormalizeLangValue ( &zGenericLang );
|
|
NormalizeLangValue ( &zSpecificLang );
|
|
|
|
XMP_StringPtr genericLang = zGenericLang.c_str();
|
|
XMP_StringPtr specificLang = zSpecificLang.c_str();
|
|
|
|
XMP_ExpandedXPath arrayPath;
|
|
ExpandXPath ( schemaNS, arrayName, &arrayPath );
|
|
|
|
const XMP_Node * arrayNode = FindConstNode ( &tree, arrayPath ); // *** This expand/find idiom is used in 3 Getters.
|
|
if ( arrayNode == 0 ) return false; // *** Should extract it into a local utility.
|
|
|
|
XMP_CLTMatch match;
|
|
const XMP_Node * itemNode;
|
|
|
|
match = ChooseLocalizedText ( arrayNode, genericLang, specificLang, &itemNode );
|
|
if ( match == kXMP_CLT_NoValues ) return false;
|
|
|
|
*actualLang = itemNode->qualifiers[0]->value.c_str();
|
|
*langSize = itemNode->qualifiers[0]->value.size();
|
|
*itemValue = itemNode->value.c_str();
|
|
*valueSize = itemNode->value.size();
|
|
*options = itemNode->options;
|
|
|
|
return true;
|
|
|
|
} // GetLocalizedText
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// SetLocalizedText
|
|
// ----------------
|
|
|
|
void
|
|
XMPMeta::SetLocalizedText ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr arrayName,
|
|
XMP_StringPtr _genericLang,
|
|
XMP_StringPtr _specificLang,
|
|
XMP_StringPtr itemValue,
|
|
XMP_OptionBits options )
|
|
{
|
|
options = options; // Avoid unused parameter warning.
|
|
|
|
XMP_Assert ( (schemaNS != 0) && (arrayName != 0) && (_genericLang != 0) && (_specificLang != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_VarString zGenericLang ( _genericLang );
|
|
XMP_VarString zSpecificLang ( _specificLang );
|
|
NormalizeLangValue ( &zGenericLang );
|
|
NormalizeLangValue ( &zSpecificLang );
|
|
|
|
XMP_StringPtr genericLang = zGenericLang.c_str();
|
|
XMP_StringPtr specificLang = zSpecificLang.c_str();
|
|
|
|
XMP_ExpandedXPath arrayPath;
|
|
ExpandXPath ( schemaNS, arrayName, &arrayPath );
|
|
|
|
// Find the array node and set the options if it was just created.
|
|
XMP_Node * arrayNode = FindNode ( &tree, arrayPath, kXMP_CreateNodes,
|
|
(kXMP_PropValueIsArray | kXMP_PropArrayIsOrdered | kXMP_PropArrayIsAlternate) );
|
|
if ( arrayNode == 0 ) XMP_Throw ( "Failed to find or create array node", kXMPErr_BadXPath );
|
|
if ( ! XMP_ArrayIsAltText(arrayNode->options) ) {
|
|
if ( arrayNode->children.empty() && XMP_ArrayIsAlternate(arrayNode->options) ) {
|
|
arrayNode->options |= kXMP_PropArrayIsAltText;
|
|
} else {
|
|
XMP_Throw ( "Localized text array is not alt-text", kXMPErr_BadXPath );
|
|
}
|
|
}
|
|
|
|
// Make sure the x-default item, if any, is first.
|
|
|
|
size_t itemNum, itemLim;
|
|
XMP_Node * xdItem = 0;
|
|
bool haveXDefault = false;
|
|
|
|
for ( itemNum = 0, itemLim = arrayNode->children.size(); itemNum < itemLim; ++itemNum ) {
|
|
XMP_Node * currItem = arrayNode->children[itemNum];
|
|
XMP_Assert ( XMP_PropHasLang(currItem->options) );
|
|
if ( currItem->qualifiers.empty() || (currItem->qualifiers[0]->name != "xml:lang") ) {
|
|
XMP_Throw ( "Language qualifier must be first", kXMPErr_BadXPath );
|
|
}
|
|
if ( currItem->qualifiers[0]->value == "x-default" ) {
|
|
xdItem = currItem;
|
|
haveXDefault = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( haveXDefault && (itemNum != 0) ) {
|
|
XMP_Assert ( arrayNode->children[itemNum]->qualifiers[0]->value == "x-default" );
|
|
XMP_Node * temp = arrayNode->children[0];
|
|
arrayNode->children[0] = arrayNode->children[itemNum];
|
|
arrayNode->children[itemNum] = temp;
|
|
}
|
|
|
|
// Find the appropriate item. ChooseLocalizedText will make sure the array is a language alternative.
|
|
|
|
const XMP_Node * cItemNode; // ! ChooseLocalizedText returns a pointer to a const node.
|
|
XMP_CLTMatch match = ChooseLocalizedText ( arrayNode, genericLang, specificLang, &cItemNode );
|
|
XMP_Node * itemNode = const_cast<XMP_Node*> ( cItemNode );
|
|
|
|
const bool specificXDefault = XMP_LitMatch ( specificLang, "x-default" );
|
|
|
|
switch ( match ) {
|
|
|
|
case kXMP_CLT_NoValues :
|
|
|
|
// Create the array items for the specificLang and x-default, with x-default first.
|
|
AppendLangItem ( arrayNode, "x-default", itemValue );
|
|
haveXDefault = true;
|
|
if ( ! specificXDefault ) AppendLangItem ( arrayNode, specificLang, itemValue );
|
|
break;
|
|
|
|
case kXMP_CLT_SpecificMatch :
|
|
|
|
if ( ! specificXDefault ) {
|
|
// Update the specific item, update x-default if it matches the old value.
|
|
if ( haveXDefault && (xdItem != itemNode) && (xdItem->value == itemNode->value) ) {
|
|
SetNodeValue ( xdItem, itemValue );
|
|
}
|
|
SetNodeValue ( itemNode, itemValue ); // ! Do this after the x-default check!
|
|
} else {
|
|
// Update all items whose values match the old x-default value.
|
|
XMP_Assert ( haveXDefault && (xdItem == itemNode) );
|
|
for ( itemNum = 0, itemLim = arrayNode->children.size(); itemNum < itemLim; ++itemNum ) {
|
|
XMP_Node * currItem = arrayNode->children[itemNum];
|
|
if ( (currItem == xdItem) || (currItem->value != xdItem->value) ) continue;
|
|
SetNodeValue ( currItem, itemValue );
|
|
}
|
|
SetNodeValue ( xdItem, itemValue ); // And finally do the x-default item.
|
|
}
|
|
break;
|
|
|
|
case kXMP_CLT_SingleGeneric :
|
|
|
|
// Update the generic item, update x-default if it matches the old value.
|
|
if ( haveXDefault && (xdItem != itemNode) && (xdItem->value == itemNode->value) ) {
|
|
SetNodeValue ( xdItem, itemValue );
|
|
}
|
|
SetNodeValue ( itemNode, itemValue ); // ! Do this after the x-default check!
|
|
break;
|
|
|
|
case kXMP_CLT_MultipleGeneric :
|
|
|
|
// Create the specific language, ignore x-default.
|
|
AppendLangItem ( arrayNode, specificLang, itemValue );
|
|
if ( specificXDefault ) haveXDefault = true;
|
|
break;
|
|
|
|
case kXMP_CLT_XDefault :
|
|
|
|
// Create the specific language, update x-default if it was the only item.
|
|
if ( arrayNode->children.size() == 1 ) SetNodeValue ( xdItem, itemValue );
|
|
AppendLangItem ( arrayNode, specificLang, itemValue );
|
|
break;
|
|
|
|
case kXMP_CLT_FirstItem :
|
|
|
|
// Create the specific language, don't add an x-default item.
|
|
AppendLangItem ( arrayNode, specificLang, itemValue );
|
|
if ( specificXDefault ) haveXDefault = true;
|
|
break;
|
|
|
|
default :
|
|
XMP_Throw ( "Unexpected result from ChooseLocalizedText", kXMPErr_InternalFailure );
|
|
|
|
}
|
|
|
|
// Add an x-default at the front if needed.
|
|
if ( (! haveXDefault) && (arrayNode->children.size() == 1) ) {
|
|
AppendLangItem ( arrayNode, "x-default", itemValue );
|
|
}
|
|
|
|
} // SetLocalizedText
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// GetProperty_Bool
|
|
// ----------------
|
|
|
|
bool
|
|
XMPMeta::GetProperty_Bool ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr propName,
|
|
bool * propValue,
|
|
XMP_OptionBits * options ) const
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
|
|
XMP_Assert ( (propValue != 0) && (options != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_StringPtr valueStr;
|
|
XMP_StringLen valueLen;
|
|
|
|
bool found = GetProperty ( schemaNS, propName, &valueStr, &valueLen, options );
|
|
if ( found ) {
|
|
if ( ! XMP_PropIsSimple ( *options ) ) XMP_Throw ( "Property must be simple", kXMPErr_BadXPath );
|
|
*propValue = XMPUtils::ConvertToBool ( valueStr );
|
|
}
|
|
return found;
|
|
|
|
} // GetProperty_Bool
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// GetProperty_Int
|
|
// ---------------
|
|
|
|
bool
|
|
XMPMeta::GetProperty_Int ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr propName,
|
|
XMP_Int32 * propValue,
|
|
XMP_OptionBits * options ) const
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
|
|
XMP_Assert ( (propValue != 0) && (options != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_StringPtr valueStr;
|
|
XMP_StringLen valueLen;
|
|
|
|
bool found = GetProperty ( schemaNS, propName, &valueStr, &valueLen, options );
|
|
if ( found ) {
|
|
if ( ! XMP_PropIsSimple ( *options ) ) XMP_Throw ( "Property must be simple", kXMPErr_BadXPath );
|
|
*propValue = XMPUtils::ConvertToInt ( valueStr );
|
|
}
|
|
return found;
|
|
|
|
} // GetProperty_Int
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// GetProperty_Int64
|
|
// -----------------
|
|
|
|
bool
|
|
XMPMeta::GetProperty_Int64 ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr propName,
|
|
XMP_Int64 * propValue,
|
|
XMP_OptionBits * options ) const
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
|
|
XMP_Assert ( (propValue != 0) && (options != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_StringPtr valueStr;
|
|
XMP_StringLen valueLen;
|
|
|
|
bool found = GetProperty ( schemaNS, propName, &valueStr, &valueLen, options );
|
|
if ( found ) {
|
|
if ( ! XMP_PropIsSimple ( *options ) ) XMP_Throw ( "Property must be simple", kXMPErr_BadXPath );
|
|
*propValue = XMPUtils::ConvertToInt64 ( valueStr );
|
|
}
|
|
return found;
|
|
|
|
} // GetProperty_Int64
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// GetProperty_Float
|
|
// -----------------
|
|
|
|
bool
|
|
XMPMeta::GetProperty_Float ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr propName,
|
|
double * propValue,
|
|
XMP_OptionBits * options ) const
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
|
|
XMP_Assert ( (propValue != 0) && (options != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_StringPtr valueStr;
|
|
XMP_StringLen valueLen;
|
|
|
|
bool found = GetProperty ( schemaNS, propName, &valueStr, &valueLen, options );
|
|
if ( found ) {
|
|
if ( ! XMP_PropIsSimple ( *options ) ) XMP_Throw ( "Property must be simple", kXMPErr_BadXPath );
|
|
*propValue = XMPUtils::ConvertToFloat ( valueStr );
|
|
}
|
|
return found;
|
|
|
|
} // GetProperty_Float
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// GetProperty_Date
|
|
// ----------------
|
|
|
|
bool
|
|
XMPMeta::GetProperty_Date ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr propName,
|
|
XMP_DateTime * propValue,
|
|
XMP_OptionBits * options ) const
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
|
|
XMP_Assert ( (propValue != 0) && (options != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_StringPtr valueStr;
|
|
XMP_StringLen valueLen;
|
|
|
|
bool found = GetProperty ( schemaNS, propName, &valueStr, &valueLen, options );
|
|
if ( found ) {
|
|
if ( ! XMP_PropIsSimple ( *options ) ) XMP_Throw ( "Property must be simple", kXMPErr_BadXPath );
|
|
XMPUtils::ConvertToDate ( valueStr, propValue );
|
|
}
|
|
return found;
|
|
|
|
} // GetProperty_Date
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// SetProperty_Bool
|
|
// ----------------
|
|
|
|
void
|
|
XMPMeta::SetProperty_Bool ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr propName,
|
|
bool propValue,
|
|
XMP_OptionBits options )
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_StringPtr valueStr;
|
|
XMP_StringLen valueLen;
|
|
|
|
XMPUtils::ConvertFromBool ( propValue, &valueStr, &valueLen );
|
|
SetProperty ( schemaNS, propName, valueStr, options );
|
|
|
|
} // SetProperty_Bool
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// SetProperty_Int
|
|
// ---------------
|
|
|
|
void
|
|
XMPMeta::SetProperty_Int ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr propName,
|
|
XMP_Int32 propValue,
|
|
XMP_OptionBits options )
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_StringPtr valueStr;
|
|
XMP_StringLen valueLen;
|
|
|
|
XMPUtils::ConvertFromInt ( propValue, "", &valueStr, &valueLen );
|
|
SetProperty ( schemaNS, propName, valueStr, options );
|
|
|
|
} // SetProperty_Int
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// SetProperty_Int64
|
|
// -----------------
|
|
|
|
void
|
|
XMPMeta::SetProperty_Int64 ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr propName,
|
|
XMP_Int64 propValue,
|
|
XMP_OptionBits options )
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_StringPtr valueStr;
|
|
XMP_StringLen valueLen;
|
|
|
|
XMPUtils::ConvertFromInt64 ( propValue, "", &valueStr, &valueLen );
|
|
SetProperty ( schemaNS, propName, valueStr, options );
|
|
|
|
} // SetProperty_Int64
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// SetProperty_Float
|
|
// -----------------
|
|
|
|
void
|
|
XMPMeta::SetProperty_Float ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr propName,
|
|
double propValue,
|
|
XMP_OptionBits options )
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_StringPtr valueStr;
|
|
XMP_StringLen valueLen;
|
|
|
|
XMPUtils::ConvertFromFloat ( propValue, "", &valueStr, &valueLen );
|
|
SetProperty ( schemaNS, propName, valueStr, options );
|
|
|
|
} // SetProperty_Float
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// SetProperty_Date
|
|
// ----------------
|
|
|
|
void
|
|
XMPMeta::SetProperty_Date ( XMP_StringPtr schemaNS,
|
|
XMP_StringPtr propName,
|
|
const XMP_DateTime & propValue,
|
|
XMP_OptionBits options )
|
|
{
|
|
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
|
|
|
|
XMP_StringPtr valueStr;
|
|
XMP_StringLen valueLen;
|
|
|
|
XMPUtils::ConvertFromDate ( propValue, &valueStr, &valueLen );
|
|
SetProperty ( schemaNS, propName, valueStr, options );
|
|
|
|
} // SetProperty_Date
|
|
|
|
// =================================================================================================
|
|
|