summaryrefslogtreecommitdiff
path: root/gpr/source/lib/xmp_core
diff options
context:
space:
mode:
authorluxagraf <sng@luxagraf.net>2023-06-15 15:58:59 -0500
committerluxagraf <sng@luxagraf.net>2023-06-15 15:58:59 -0500
commitab987e10f154f5536bb8fd936ae0966e909fa969 (patch)
tree9de5076f38b71ececb1bc94f8d9d19170898d603 /gpr/source/lib/xmp_core
added all my scriptssynced/master
Diffstat (limited to 'gpr/source/lib/xmp_core')
-rw-r--r--gpr/source/lib/xmp_core/BSD-License.txt32
-rw-r--r--gpr/source/lib/xmp_core/CMakeLists.txt32
-rw-r--r--gpr/source/lib/xmp_core/ExpatAdapter.cpp522
-rw-r--r--gpr/source/lib/xmp_core/ExpatAdapter.hpp59
-rw-r--r--gpr/source/lib/xmp_core/ParseRDF.cpp1459
-rw-r--r--gpr/source/lib/xmp_core/UnicodeConversions.cpp1654
-rw-r--r--gpr/source/lib/xmp_core/UnicodeConversions.hpp115
-rw-r--r--gpr/source/lib/xmp_core/UnicodeInlines.incl_cpp129
-rw-r--r--gpr/source/lib/xmp_core/WXMPIterator.cpp170
-rw-r--r--gpr/source/lib/xmp_core/WXMPMeta.cpp1191
-rw-r--r--gpr/source/lib/xmp_core/WXMPUtils.cpp634
-rw-r--r--gpr/source/lib/xmp_core/XMLParserAdapter.hpp155
-rw-r--r--gpr/source/lib/xmp_core/XML_Node.cpp473
-rw-r--r--gpr/source/lib/xmp_core/XMPCore_Impl.cpp1390
-rw-r--r--gpr/source/lib/xmp_core/XMPCore_Impl.hpp392
-rw-r--r--gpr/source/lib/xmp_core/XMPIterator.cpp637
-rw-r--r--gpr/source/lib/xmp_core/XMPIterator.hpp144
-rw-r--r--gpr/source/lib/xmp_core/XMPMeta-GetSet.cpp1309
-rw-r--r--gpr/source/lib/xmp_core/XMPMeta-Parse.cpp1277
-rw-r--r--gpr/source/lib/xmp_core/XMPMeta-Serialize.cpp1396
-rw-r--r--gpr/source/lib/xmp_core/XMPMeta.cpp1381
-rw-r--r--gpr/source/lib/xmp_core/XMPMeta.hpp428
-rw-r--r--gpr/source/lib/xmp_core/XMPUtils-FileInfo.cpp1493
-rw-r--r--gpr/source/lib/xmp_core/XMPUtils.cpp2000
-rw-r--r--gpr/source/lib/xmp_core/XMPUtils.hpp198
-rw-r--r--gpr/source/lib/xmp_core/XMP_BuildInfo.h17
-rw-r--r--gpr/source/lib/xmp_core/XMP_LibUtils.cpp705
-rw-r--r--gpr/source/lib/xmp_core/XMP_LibUtils.hpp619
-rw-r--r--gpr/source/lib/xmp_core/public/include/TXMPFiles.hpp855
-rw-r--r--gpr/source/lib/xmp_core/public/include/TXMPIterator.hpp235
-rw-r--r--gpr/source/lib/xmp_core/public/include/TXMPMeta.hpp1751
-rw-r--r--gpr/source/lib/xmp_core/public/include/TXMPUtils.hpp967
-rw-r--r--gpr/source/lib/xmp_core/public/include/XMP.hpp98
-rw-r--r--gpr/source/lib/xmp_core/public/include/XMP.incl_cpp69
-rw-r--r--gpr/source/lib/xmp_core/public/include/XMP_Const.h1560
-rw-r--r--gpr/source/lib/xmp_core/public/include/XMP_Environment.h165
-rw-r--r--gpr/source/lib/xmp_core/public/include/XMP_IO.hpp171
-rw-r--r--gpr/source/lib/xmp_core/public/include/XMP_Version.h52
-rw-r--r--gpr/source/lib/xmp_core/public/include/client-glue/TXMPFiles.incl_cpp484
-rw-r--r--gpr/source/lib/xmp_core/public/include/client-glue/TXMPIterator.incl_cpp223
-rw-r--r--gpr/source/lib/xmp_core/public/include/client-glue/TXMPMeta.incl_cpp914
-rw-r--r--gpr/source/lib/xmp_core/public/include/client-glue/TXMPUtils.incl_cpp445
-rw-r--r--gpr/source/lib/xmp_core/public/include/client-glue/WXMPFiles.hpp281
-rw-r--r--gpr/source/lib/xmp_core/public/include/client-glue/WXMPIterator.hpp74
-rw-r--r--gpr/source/lib/xmp_core/public/include/client-glue/WXMPMeta.hpp621
-rw-r--r--gpr/source/lib/xmp_core/public/include/client-glue/WXMPUtils.hpp315
-rw-r--r--gpr/source/lib/xmp_core/public/include/client-glue/WXMP_Common.hpp128
47 files changed, 29419 insertions, 0 deletions
diff --git a/gpr/source/lib/xmp_core/BSD-License.txt b/gpr/source/lib/xmp_core/BSD-License.txt
new file mode 100644
index 0000000..07b967c
--- /dev/null
+++ b/gpr/source/lib/xmp_core/BSD-License.txt
@@ -0,0 +1,32 @@
+The BSD License
+
+Copyright (c) 1999 - 2014, Adobe Systems Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems Incorporated, nor the names of its
+contributors may be used to endorse or promote products derived from this
+software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/gpr/source/lib/xmp_core/CMakeLists.txt b/gpr/source/lib/xmp_core/CMakeLists.txt
new file mode 100644
index 0000000..5bee2e4
--- /dev/null
+++ b/gpr/source/lib/xmp_core/CMakeLists.txt
@@ -0,0 +1,32 @@
+# library
+set( LIB_NAME xmp_core )
+
+# get source files
+file( GLOB BASE_SRC_FILES "*.cpp" )
+
+# get include files
+file( GLOB BASE_INC_FILES "*.h" "public/include/*.h" "public/include/*.hpp" "public/include/client-glue/*.hpp" )
+
+# get all source files
+set( SRC_FILES ${BASE_SRC_FILES} )
+
+# get all include files
+set( INC_FILES ${BASE_INC_FILES} )
+
+# add include files from other folders
+include_directories( "../md5_lib" )
+include_directories( "../expat_lib" )
+include_directories( "public/include" )
+
+# library
+add_library( ${LIB_NAME} STATIC ${SRC_FILES} ${INC_FILES} )
+
+# define compile time definitions
+target_compile_definitions( ${LIB_NAME} PUBLIC XML_STATIC=1 )
+
+SET(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libstdc++")
+
+target_link_libraries( ${LIB_NAME} )
+
+# set the folder where to place the projects
+set_target_properties( ${LIB_NAME} PROPERTIES FOLDER lib )
diff --git a/gpr/source/lib/xmp_core/ExpatAdapter.cpp b/gpr/source/lib/xmp_core/ExpatAdapter.cpp
new file mode 100644
index 0000000..c0388c3
--- /dev/null
+++ b/gpr/source/lib/xmp_core/ExpatAdapter.cpp
@@ -0,0 +1,522 @@
+// =================================================================================================
+// Copyright 2005 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.
+// =================================================================================================
+
+#include "public/include/XMP_Environment.h" // ! Must be the first #include!
+#include "XMPCore_Impl.hpp"
+
+#include "ExpatAdapter.hpp"
+#include "XMPMeta.hpp"
+
+#include "expat.h"
+#include <string.h>
+
+using namespace std;
+
+#if XMP_WinBuild
+ #pragma warning ( disable : 4996 ) // '...' was declared deprecated
+#endif
+
+// *** Set memory handlers.
+
+#ifndef DumpXMLParseEvents
+ #define DumpXMLParseEvents 0
+#endif
+
+#define FullNameSeparator '@'
+
+// =================================================================================================
+
+static void StartNamespaceDeclHandler ( void * userData, XMP_StringPtr prefix, XMP_StringPtr uri );
+static void EndNamespaceDeclHandler ( void * userData, XMP_StringPtr prefix );
+
+static void StartElementHandler ( void * userData, XMP_StringPtr name, XMP_StringPtr* attrs );
+static void EndElementHandler ( void * userData, XMP_StringPtr name );
+
+static void CharacterDataHandler ( void * userData, XMP_StringPtr cData, int len );
+static void StartCdataSectionHandler ( void * userData );
+static void EndCdataSectionHandler ( void * userData );
+
+static void ProcessingInstructionHandler ( void * userData, XMP_StringPtr target, XMP_StringPtr data );
+static void CommentHandler ( void * userData, XMP_StringPtr comment );
+
+#if BanAllEntityUsage
+
+ // For now we do this by banning DOCTYPE entirely. This is easy and consistent with what is
+ // available in recent Java XML parsers. Another, somewhat less drastic, approach would be to
+ // ban all entity declarations. We can't allow declarations and ban references, Expat does not
+ // call the SkippedEntityHandler for references in attribute values.
+
+ // ! Standard entities (&amp;, &lt;, &gt;, &quot;, &apos;, and numeric character references) are
+ // ! not banned. Expat handles them transparently no matter what.
+
+ static void StartDoctypeDeclHandler ( void * userData, XMP_StringPtr doctypeName,
+ XMP_StringPtr sysid, XMP_StringPtr pubid, int has_internal_subset );
+
+#endif
+
+// =================================================================================================
+
+extern "C" ExpatAdapter * XMP_NewExpatAdapter ( bool useGlobalNamespaces )
+{
+
+ return new ExpatAdapter ( useGlobalNamespaces );
+
+} // XMP_NewExpatAdapter
+
+// =================================================================================================
+
+ExpatAdapter::ExpatAdapter ( bool useGlobalNamespaces ) : parser(0), registeredNamespaces(0)
+{
+
+ #if XMP_DebugBuild
+ this->elemNesting = 0;
+ #if DumpXMLParseEvents
+ if ( this->parseLog == 0 ) this->parseLog = stdout;
+ #endif
+ #endif
+
+ this->parser = XML_ParserCreateNS ( 0, FullNameSeparator );
+ if ( this->parser == 0 ) {
+ XMP_Error error(kXMPErr_NoMemory, "Failure creating Expat parser" );
+ this->NotifyClient ( kXMPErrSev_ProcessFatal, error );
+ }else{
+ if ( useGlobalNamespaces ) {
+ this->registeredNamespaces = sRegisteredNamespaces;
+ } else {
+ this->registeredNamespaces = new XMP_NamespaceTable ( *sRegisteredNamespaces );
+ }
+
+ XML_SetUserData ( this->parser, this );
+
+ XML_SetNamespaceDeclHandler ( this->parser, StartNamespaceDeclHandler, EndNamespaceDeclHandler );
+ XML_SetElementHandler ( this->parser, StartElementHandler, EndElementHandler );
+
+ XML_SetCharacterDataHandler ( this->parser, CharacterDataHandler );
+ XML_SetCdataSectionHandler ( this->parser, StartCdataSectionHandler, EndCdataSectionHandler );
+
+ XML_SetProcessingInstructionHandler ( this->parser, ProcessingInstructionHandler );
+ XML_SetCommentHandler ( this->parser, CommentHandler );
+
+ #if BanAllEntityUsage
+ XML_SetStartDoctypeDeclHandler ( this->parser, StartDoctypeDeclHandler );
+ isAborted = false;
+ #endif
+
+ this->parseStack.push_back ( &this->tree ); // Push the XML root node.
+ }
+} // ExpatAdapter::ExpatAdapter
+
+// =================================================================================================
+
+ExpatAdapter::~ExpatAdapter()
+{
+
+ if ( this->parser != 0 ) XML_ParserFree ( this->parser );
+ this->parser = 0;
+
+ if ( this->registeredNamespaces != sRegisteredNamespaces ) delete ( this->registeredNamespaces );
+ this->registeredNamespaces = 0;
+
+} // ExpatAdapter::~ExpatAdapter
+
+// =================================================================================================
+
+#if XMP_DebugBuild
+ static XMP_VarString sExpatMessage;
+#endif
+
+static const char * kOneSpace = " ";
+
+void ExpatAdapter::ParseBuffer ( const void * buffer, size_t length, bool last /* = true */ )
+{
+ enum XML_Status status;
+
+ if ( length == 0 ) { // Expat does not like empty buffers.
+ if ( ! last ) return;
+ buffer = kOneSpace;
+ length = 1;
+ }
+
+ status = XML_Parse ( this->parser, (const char *)buffer, length, last );
+
+ #if BanAllEntityUsage
+ if ( this->isAborted ) {
+ XMP_Error error(kXMPErr_BadXML, "DOCTYPE is not allowed" )
+ this->NotifyClient ( kXMPErrSev_Recoverable, error );
+ }
+ #endif
+
+ if ( status != XML_STATUS_OK ) {
+
+ XMP_StringPtr errMsg = "XML parsing failure";
+
+ #if 0 // XMP_DebugBuild // Disable for now to make test output uniform. Restore later with thread safety.
+
+ // *** This is a good candidate for a callback error notification mechanism.
+ // *** This code is not thread safe, the sExpatMessage isn't locked. But that's OK for debug usage.
+
+ enum XML_Error expatErr = XML_GetErrorCode ( this->parser );
+ const char * expatMsg = XML_ErrorString ( expatErr );
+ int errLine = XML_GetCurrentLineNumber ( this->parser );
+
+ char msgBuffer[1000];
+ // AUDIT: Use of sizeof(msgBuffer) for snprintf length is safe.
+ snprintf ( msgBuffer, sizeof(msgBuffer), "# Expat error %d at line %d, \"%s\"", expatErr, errLine, expatMsg );
+ sExpatMessage = msgBuffer;
+ errMsg = sExpatMessage.c_str();
+
+ #if DumpXMLParseEvents
+ if ( this->parseLog != 0 ) fprintf ( this->parseLog, "%s\n", errMsg, expatErr, errLine, expatMsg );
+ #endif
+
+ #endif
+
+ XMP_Error error(kXMPErr_BadXML, errMsg);
+ this->NotifyClient ( kXMPErrSev_Recoverable, error );
+
+ }
+
+} // ExpatAdapter::ParseBuffer
+
+// =================================================================================================
+// =================================================================================================
+
+#if XMP_DebugBuild & DumpXMLParseEvents
+
+ static inline void PrintIndent ( FILE * file, size_t count )
+ {
+ for ( ; count > 0; --count ) fprintf ( file, " " );
+ }
+
+#endif
+
+// =================================================================================================
+
+static void SetQualName ( ExpatAdapter * thiz, XMP_StringPtr fullName, XML_Node * node )
+{
+ // Expat delivers the full name as a catenation of namespace URI, separator, and local name.
+
+ // As a compatibility hack, an "about" or "ID" attribute of an rdf:Description element is
+ // changed to "rdf:about" or rdf:ID. Easier done here than in the RDF recognizer.
+
+ // As a bug fix hack, change a URI of "http://purl.org/dc/1.1/" to ""http://purl.org/dc/elements/1.1/.
+ // Early versions of Flash that put XMP in SWF used a bad URI for the dc: namespace.
+
+ // ! This code presumes the RDF namespace prefix is "rdf".
+
+ size_t sepPos = strlen(fullName);
+ for ( --sepPos; sepPos > 0; --sepPos ) {
+ if ( fullName[sepPos] == FullNameSeparator ) break;
+ }
+
+ if ( fullName[sepPos] == FullNameSeparator ) {
+
+ XMP_StringPtr prefix;
+ XMP_StringLen prefixLen;
+ XMP_StringPtr localPart = fullName + sepPos + 1;
+
+ node->ns.assign ( fullName, sepPos );
+ if ( node->ns == "http://purl.org/dc/1.1/" ) node->ns = "http://purl.org/dc/elements/1.1/";
+
+ bool found = thiz->registeredNamespaces->GetPrefix ( node->ns.c_str(), &prefix, &prefixLen );
+ if ( ! found ) {
+ XMP_Error error(kXMPErr_ExternalFailure, "Unknown URI in Expat full name" );
+ thiz->NotifyClient ( kXMPErrSev_OperationFatal, error );
+ }
+ node->nsPrefixLen = prefixLen; // ! Includes the ':'.
+
+ node->name = prefix;
+ node->name += localPart;
+
+ } else {
+
+ node->name = fullName; // The name is not in a namespace.
+
+ if ( node->parent->name == "rdf:Description" ) {
+ if ( node->name == "about" ) {
+ node->ns = kXMP_NS_RDF;
+ node->name = "rdf:about";
+ node->nsPrefixLen = 4; // ! Include the ':'.
+ } else if ( node->name == "ID" ) {
+ node->ns = kXMP_NS_RDF;
+ node->name = "rdf:ID";
+ node->nsPrefixLen = 4; // ! Include the ':'.
+ }
+ }
+
+ }
+
+} // SetQualName
+
+// =================================================================================================
+
+static void StartNamespaceDeclHandler ( void * userData, XMP_StringPtr prefix, XMP_StringPtr uri )
+{
+ IgnoreParam(userData);
+
+ // As a bug fix hack, change a URI of "http://purl.org/dc/1.1/" to ""http://purl.org/dc/elements/1.1/.
+ // Early versions of Flash that put XMP in SWF used a bad URI for the dc: namespace.
+
+ ExpatAdapter * thiz = (ExpatAdapter*)userData;
+
+ if ( prefix == 0 ) prefix = "_dflt_"; // Have default namespace.
+ if ( uri == 0 ) return; // Ignore, have xmlns:pre="", no URI to register.
+
+ #if XMP_DebugBuild & DumpXMLParseEvents
+ if ( thiz->parseLog != 0 ) {
+ PrintIndent ( thiz->parseLog, thiz->elemNesting );
+ fprintf ( thiz->parseLog, "StartNamespace: %s - \"%s\"\n", prefix, uri );
+ }
+ #endif
+
+ if ( XMP_LitMatch ( uri, "http://purl.org/dc/1.1/" ) ) uri = "http://purl.org/dc/elements/1.1/";
+ (void) thiz->registeredNamespaces->Define ( uri, prefix, 0, 0 );
+
+} // StartNamespaceDeclHandler
+
+// =================================================================================================
+
+static void EndNamespaceDeclHandler ( void * userData, XMP_StringPtr prefix )
+{
+ IgnoreParam(userData);
+
+ #if XMP_DebugBuild & DumpXMLParseEvents // Avoid unused variable warning.
+ ExpatAdapter * thiz = (ExpatAdapter*)userData;
+ #endif
+
+ if ( prefix == 0 ) prefix = "_dflt_"; // Have default namespace.
+
+ #if XMP_DebugBuild & DumpXMLParseEvents
+ if ( thiz->parseLog != 0 ) {
+ PrintIndent ( thiz->parseLog, thiz->elemNesting );
+ fprintf ( thiz->parseLog, "EndNamespace: %s\n", prefix );
+ }
+ #endif
+
+ // ! Nothing to do, Expat has done all of the XML processing.
+
+} // EndNamespaceDeclHandler
+
+// =================================================================================================
+
+static void StartElementHandler ( void * userData, XMP_StringPtr name, XMP_StringPtr* attrs )
+{
+ XMP_Assert ( attrs != 0 );
+ ExpatAdapter * thiz = (ExpatAdapter*)userData;
+
+ size_t attrCount = 0;
+ for ( XMP_StringPtr* a = attrs; *a != 0; ++a ) ++attrCount;
+ if ( (attrCount & 1) != 0 ) {
+ XMP_Error error(kXMPErr_ExternalFailure, "Expat attribute info has odd length");
+ thiz->NotifyClient ( kXMPErrSev_OperationFatal, error );
+ }
+ attrCount = attrCount/2; // They are name/value pairs.
+
+ #if XMP_DebugBuild & DumpXMLParseEvents
+ if ( thiz->parseLog != 0 ) {
+ PrintIndent ( thiz->parseLog, thiz->elemNesting );
+ fprintf ( thiz->parseLog, "StartElement: %s, %d attrs", name, attrCount );
+ for ( XMP_StringPtr* attr = attrs; *attr != 0; attr += 2 ) {
+ XMP_StringPtr attrName = *attr;
+ XMP_StringPtr attrValue = *(attr+1);
+ fprintf ( thiz->parseLog, ", %s = \"%s\"", attrName, attrValue );
+ }
+ fprintf ( thiz->parseLog, "\n" );
+ }
+ #endif
+
+ XML_Node * parentNode = thiz->parseStack.back();
+ XML_Node * elemNode = new XML_Node ( parentNode, "", kElemNode );
+
+ SetQualName ( thiz, name, elemNode );
+
+ for ( XMP_StringPtr* attr = attrs; *attr != 0; attr += 2 ) {
+
+ XMP_StringPtr attrName = *attr;
+ XMP_StringPtr attrValue = *(attr+1);
+ XML_Node * attrNode = new XML_Node ( elemNode, "", kAttrNode );
+
+ SetQualName ( thiz, attrName, attrNode );
+ attrNode->value = attrValue;
+ if ( attrNode->name == "xml:lang" ) NormalizeLangValue ( &attrNode->value );
+ elemNode->attrs.push_back ( attrNode );
+
+ }
+
+ parentNode->content.push_back ( elemNode );
+ thiz->parseStack.push_back ( elemNode );
+
+ if ( elemNode->name == "rdf:RDF" ) {
+ thiz->rootNode = elemNode;
+ ++thiz->rootCount;
+ }
+ #if XMP_DebugBuild
+ ++thiz->elemNesting;
+ #endif
+
+} // StartElementHandler
+
+// =================================================================================================
+
+static void EndElementHandler ( void * userData, XMP_StringPtr name )
+{
+ IgnoreParam(name);
+
+ ExpatAdapter * thiz = (ExpatAdapter*)userData;
+
+ #if XMP_DebugBuild
+ --thiz->elemNesting;
+ #endif
+ (void) thiz->parseStack.pop_back();
+
+ #if XMP_DebugBuild & DumpXMLParseEvents
+ if ( thiz->parseLog != 0 ) {
+ PrintIndent ( thiz->parseLog, thiz->elemNesting );
+ fprintf ( thiz->parseLog, "EndElement: %s\n", name );
+ }
+ #endif
+
+} // EndElementHandler
+
+// =================================================================================================
+
+static void CharacterDataHandler ( void * userData, XMP_StringPtr cData, int len )
+{
+ ExpatAdapter * thiz = (ExpatAdapter*)userData;
+
+ if ( (cData == 0) || (len == 0) ) { cData = ""; len = 0; }
+
+ #if XMP_DebugBuild & DumpXMLParseEvents
+ if ( thiz->parseLog != 0 ) {
+ PrintIndent ( thiz->parseLog, thiz->elemNesting );
+ fprintf ( thiz->parseLog, "CharContent: \"" );
+ for ( int i = 0; i < len; ++i ) fprintf ( thiz->parseLog, "%c", cData[i] );
+ fprintf ( thiz->parseLog, "\"\n" );
+ }
+ #endif
+
+ XML_Node * parentNode = thiz->parseStack.back();
+ XML_Node * cDataNode = new XML_Node ( parentNode, "", kCDataNode );
+
+ cDataNode->value.assign ( cData, len );
+ parentNode->content.push_back ( cDataNode );
+
+} // CharacterDataHandler
+
+// =================================================================================================
+
+static void StartCdataSectionHandler ( void * userData )
+{
+ IgnoreParam(userData);
+
+ #if XMP_DebugBuild & DumpXMLParseEvents // Avoid unused variable warning.
+ ExpatAdapter * thiz = (ExpatAdapter*)userData;
+ #endif
+
+ #if XMP_DebugBuild & DumpXMLParseEvents
+ if ( thiz->parseLog != 0 ) {
+ PrintIndent ( thiz->parseLog, thiz->elemNesting );
+ fprintf ( thiz->parseLog, "StartCDATA\n" );
+ }
+ #endif
+
+ // *** Since markup isn't recognized inside CDATA, this affects XMP's double escaping.
+
+} // StartCdataSectionHandler
+
+// =================================================================================================
+
+static void EndCdataSectionHandler ( void * userData )
+{
+ IgnoreParam(userData);
+
+ #if XMP_DebugBuild & DumpXMLParseEvents // Avoid unused variable warning.
+ ExpatAdapter * thiz = (ExpatAdapter*)userData;
+ #endif
+
+ #if XMP_DebugBuild & DumpXMLParseEvents
+ if ( thiz->parseLog != 0 ) {
+ PrintIndent ( thiz->parseLog, thiz->elemNesting );
+ fprintf ( thiz->parseLog, "EndCDATA\n" );
+ }
+ #endif
+
+} // EndCdataSectionHandler
+
+// =================================================================================================
+
+static void ProcessingInstructionHandler ( void * userData, XMP_StringPtr target, XMP_StringPtr data )
+{
+ XMP_Assert ( target != 0 );
+ ExpatAdapter * thiz = (ExpatAdapter*)userData;
+
+ if ( ! XMP_LitMatch ( target, "xpacket" ) ) return; // Ignore all PIs except the XMP packet wrapper.
+ if ( data == 0 ) data = "";
+
+ #if XMP_DebugBuild & DumpXMLParseEvents
+ if ( thiz->parseLog != 0 ) {
+ PrintIndent ( thiz->parseLog, thiz->elemNesting );
+ fprintf ( thiz->parseLog, "PI: %s - \"%s\"\n", target, data );
+ }
+ #endif
+
+ XML_Node * parentNode = thiz->parseStack.back();
+ XML_Node * piNode = new XML_Node ( parentNode, target, kPINode );
+
+ piNode->value.assign ( data );
+ parentNode->content.push_back ( piNode );
+
+} // ProcessingInstructionHandler
+
+// =================================================================================================
+
+static void CommentHandler ( void * userData, XMP_StringPtr comment )
+{
+ IgnoreParam(userData);
+
+ #if XMP_DebugBuild & DumpXMLParseEvents // Avoid unused variable warning.
+ ExpatAdapter * thiz = (ExpatAdapter*)userData;
+ #endif
+
+ if ( comment == 0 ) comment = "";
+
+ #if XMP_DebugBuild & DumpXMLParseEvents
+ if ( thiz->parseLog != 0 ) {
+ PrintIndent ( thiz->parseLog, thiz->elemNesting );
+ fprintf ( thiz->parseLog, "Comment: \"%s\"\n", comment );
+ }
+ #endif
+
+ // ! Comments are ignored.
+
+} // CommentHandler
+
+// =================================================================================================
+
+#if BanAllEntityUsage
+static void StartDoctypeDeclHandler ( void * userData, XMP_StringPtr doctypeName,
+ XMP_StringPtr sysid, XMP_StringPtr pubid, int has_internal_subset )
+{
+ IgnoreParam(userData);
+
+ ExpatAdapter * thiz = (ExpatAdapter*)userData;
+
+ #if XMP_DebugBuild & DumpXMLParseEvents // Avoid unused variable warning.
+ if ( thiz->parseLog != 0 ) {
+ PrintIndent ( thiz->parseLog, thiz->elemNesting );
+ fprintf ( thiz->parseLog, "DocType: \"%s\"\n", doctypeName );
+ }
+ #endif
+
+ thiz->isAborted = true; // ! Can't throw an exception across the plain C Expat frames.
+ (void) XML_StopParser ( thiz->parser, XML_FALSE /* not resumable */ );
+
+} // StartDoctypeDeclHandler
+#endif
+
+// =================================================================================================
diff --git a/gpr/source/lib/xmp_core/ExpatAdapter.hpp b/gpr/source/lib/xmp_core/ExpatAdapter.hpp
new file mode 100644
index 0000000..4e03436
--- /dev/null
+++ b/gpr/source/lib/xmp_core/ExpatAdapter.hpp
@@ -0,0 +1,59 @@
+#ifndef __ExpatAdapter_hpp__
+#define __ExpatAdapter_hpp__
+
+// =================================================================================================
+// Copyright 2005 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.
+// =================================================================================================
+
+#include "public/include/XMP_Environment.h" // ! Must be the first #include!
+#include "XMLParserAdapter.hpp"
+
+// =================================================================================================
+// Derived XML parser adapter for Expat.
+// =================================================================================================
+
+#ifndef BanAllEntityUsage
+ #define BanAllEntityUsage 0
+#endif
+
+struct XML_ParserStruct; // ! Hack to avoid exposing expat.h to all clients.
+typedef struct XML_ParserStruct *XML_Parser;
+
+class ExpatAdapter : public XMLParserAdapter {
+public:
+
+ XML_Parser parser;
+ XMP_NamespaceTable * registeredNamespaces;
+
+ #if BanAllEntityUsage
+ bool isAborted;
+ #endif
+
+ #if XMP_DebugBuild
+ size_t elemNesting;
+ #endif
+
+ static const bool kUseGlobalNamespaces = true;
+ static const bool kUseLocalNamespaces = false;
+
+ ExpatAdapter ( bool useGlobalNamespaces );
+ virtual ~ExpatAdapter();
+
+ void ParseBuffer ( const void * buffer, size_t length, bool last = true );
+
+private:
+
+ ExpatAdapter() : registeredNamespaces(0) {}; // ! Force use of constructor with namespace parameter.
+
+};
+
+extern "C" ExpatAdapter *
+XMP_PUBLIC XMP_NewExpatAdapter ( bool useGlobalNamespaces );
+
+// =================================================================================================
+
+#endif // __ExpatAdapter_hpp__
diff --git a/gpr/source/lib/xmp_core/ParseRDF.cpp b/gpr/source/lib/xmp_core/ParseRDF.cpp
new file mode 100644
index 0000000..0b69e31
--- /dev/null
+++ b/gpr/source/lib/xmp_core/ParseRDF.cpp
@@ -0,0 +1,1459 @@
+// =================================================================================================
+// Copyright 2004 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.
+// =================================================================================================
+
+#include "public/include/XMP_Environment.h" // ! This must be the first include!
+#include "XMPCore_Impl.hpp"
+#include "XMPMeta.hpp"
+#include "ExpatAdapter.hpp"
+
+#include <cstring>
+
+#if DEBUG
+ #include <iostream>
+#endif
+
+using namespace std;
+
+#if XMP_WinBuild
+ #pragma warning ( disable : 4189 ) // local variable is initialized but not referenced
+ #pragma warning ( disable : 4505 ) // unreferenced local function has been removed
+#endif
+
+// =================================================================================================
+
+// *** This might be faster and use less memory as a state machine. A big advantage of building an
+// *** XML tree though is easy lookahead during the recursive descent processing.
+
+// *** It would be nice to give a line number or byte offset in the exception messages.
+
+
+// 7 RDF/XML Grammar (from http://www.w3.org/TR/rdf-syntax-grammar/#section-Infoset-Grammar)
+//
+// 7.1 Grammar summary
+//
+// 7.2.2 coreSyntaxTerms
+// rdf:RDF | rdf:ID | rdf:about | rdf:parseType | rdf:resource | rdf:nodeID | rdf:datatype
+//
+// 7.2.3 syntaxTerms
+// coreSyntaxTerms | rdf:Description | rdf:li
+//
+// 7.2.4 oldTerms
+// rdf:aboutEach | rdf:aboutEachPrefix | rdf:bagID
+//
+// 7.2.5 nodeElementURIs
+// anyURI - ( coreSyntaxTerms | rdf:li | oldTerms )
+//
+// 7.2.6 propertyElementURIs
+// anyURI - ( coreSyntaxTerms | rdf:Description | oldTerms )
+//
+// 7.2.7 propertyAttributeURIs
+// anyURI - ( coreSyntaxTerms | rdf:Description | rdf:li | oldTerms )
+//
+// 7.2.8 doc
+// root ( document-element == RDF, children == list ( RDF ) )
+//
+// 7.2.9 RDF
+// start-element ( URI == rdf:RDF, attributes == set() )
+// nodeElementList
+// end-element()
+//
+// 7.2.10 nodeElementList
+// ws* ( nodeElement ws* )*
+//
+// 7.2.11 nodeElement
+// start-element ( URI == nodeElementURIs,
+// attributes == set ( ( idAttr | nodeIdAttr | aboutAttr )?, propertyAttr* ) )
+// propertyEltList
+// end-element()
+//
+// 7.2.12 ws
+// A text event matching white space defined by [XML] definition White Space Rule [3] S in section Common Syntactic Constructs.
+//
+// 7.2.13 propertyEltList
+// ws* ( propertyElt ws* )*
+//
+// 7.2.14 propertyElt
+// resourcePropertyElt | literalPropertyElt | parseTypeLiteralPropertyElt |
+// parseTypeResourcePropertyElt | parseTypeCollectionPropertyElt | parseTypeOtherPropertyElt | emptyPropertyElt
+//
+// 7.2.15 resourcePropertyElt
+// start-element ( URI == propertyElementURIs, attributes == set ( idAttr? ) )
+// ws* nodeElement ws*
+// end-element()
+//
+// 7.2.16 literalPropertyElt
+// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, datatypeAttr?) )
+// text()
+// end-element()
+//
+// 7.2.17 parseTypeLiteralPropertyElt
+// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseLiteral ) )
+// literal
+// end-element()
+//
+// 7.2.18 parseTypeResourcePropertyElt
+// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseResource ) )
+// propertyEltList
+// end-element()
+//
+// 7.2.19 parseTypeCollectionPropertyElt
+// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseCollection ) )
+// nodeElementList
+// end-element()
+//
+// 7.2.20 parseTypeOtherPropertyElt
+// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseOther ) )
+// propertyEltList
+// end-element()
+//
+// 7.2.21 emptyPropertyElt
+// start-element ( URI == propertyElementURIs,
+// attributes == set ( idAttr?, ( resourceAttr | nodeIdAttr )?, propertyAttr* ) )
+// end-element()
+//
+// 7.2.22 idAttr
+// attribute ( URI == rdf:ID, string-value == rdf-id )
+//
+// 7.2.23 nodeIdAttr
+// attribute ( URI == rdf:nodeID, string-value == rdf-id )
+//
+// 7.2.24 aboutAttr
+// attribute ( URI == rdf:about, string-value == URI-reference )
+//
+// 7.2.25 propertyAttr
+// attribute ( URI == propertyAttributeURIs, string-value == anyString )
+//
+// 7.2.26 resourceAttr
+// attribute ( URI == rdf:resource, string-value == URI-reference )
+//
+// 7.2.27 datatypeAttr
+// attribute ( URI == rdf:datatype, string-value == URI-reference )
+//
+// 7.2.28 parseLiteral
+// attribute ( URI == rdf:parseType, string-value == "Literal")
+//
+// 7.2.29 parseResource
+// attribute ( URI == rdf:parseType, string-value == "Resource")
+//
+// 7.2.30 parseCollection
+// attribute ( URI == rdf:parseType, string-value == "Collection")
+//
+// 7.2.31 parseOther
+// attribute ( URI == rdf:parseType, string-value == anyString - ("Resource" | "Literal" | "Collection") )
+//
+// 7.2.32 URI-reference
+// An RDF URI Reference.
+//
+// 7.2.33 literal
+// Any XML element content that is allowed according to [XML] definition Content of Elements Rule [43] content
+// in section 3.1 Start-Tags, End-Tags, and Empty-Element Tags.
+//
+// 7.2.34 rdf-id
+// An attribute string-value matching any legal [XML-NS] token NCName.
+
+
+// =================================================================================================
+// Primary Parsing Functions
+// =========================
+//
+// Each of these is responsible for recognizing an RDF syntax production and adding the appropriate
+// structure to the XMP tree. They simply return for success, failures will throw an exception. The
+// class exists only to provide access to the error notification object.
+
+class RDF_Parser {
+public:
+
+ void RDF ( XMP_Node * xmpTree, const XML_Node & xmlNode );
+
+ void NodeElementList ( XMP_Node * xmpParent, const XML_Node & xmlParent, bool isTopLevel );
+
+ void NodeElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
+
+ void NodeElementAttrs ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
+
+ void PropertyElementList ( XMP_Node * xmpParent, const XML_Node & xmlParent, bool isTopLevel );
+
+ void PropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
+
+ void ResourcePropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
+
+ void LiteralPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
+
+ void ParseTypeLiteralPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
+
+ void ParseTypeResourcePropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
+
+ void ParseTypeCollectionPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
+
+ void ParseTypeOtherPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
+
+ void EmptyPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
+
+ RDF_Parser ( XMPMeta::ErrorCallbackInfo * ec ) : errorCallback(ec) {};
+
+private:
+
+ RDF_Parser() {
+
+ errorCallback = NULL;
+
+ }; // Hidden on purpose.
+
+ XMPMeta::ErrorCallbackInfo * errorCallback;
+
+ XMP_Node * AddChildNode ( XMP_Node * xmpParent, const XML_Node & xmlNode, const XMP_StringPtr value, bool isTopLevel );
+
+ XMP_Node * AddQualifierNode ( XMP_Node * xmpParent, const XMP_VarString & name, const XMP_VarString & value );
+
+ XMP_Node * AddQualifierNode ( XMP_Node * xmpParent, const XML_Node & attr );
+
+ void FixupQualifiedNode ( XMP_Node * xmpParent );
+
+};
+
+enum { kIsTopLevel = true, kNotTopLevel = false };
+
+// =================================================================================================
+
+typedef XMP_Uns8 RDFTermKind;
+
+// *** Logic might be safer with just masks.
+
+enum {
+ kRDFTerm_Other = 0,
+ kRDFTerm_RDF = 1, // Start of coreSyntaxTerms.
+ kRDFTerm_ID = 2,
+ kRDFTerm_about = 3,
+ kRDFTerm_parseType = 4,
+ kRDFTerm_resource = 5,
+ kRDFTerm_nodeID = 6,
+ kRDFTerm_datatype = 7, // End of coreSyntaxTerms.
+ kRDFTerm_Description = 8, // Start of additions for syntaxTerms.
+ kRDFTerm_li = 9, // End of of additions for syntaxTerms.
+ kRDFTerm_aboutEach = 10, // Start of oldTerms.
+ kRDFTerm_aboutEachPrefix = 11,
+ kRDFTerm_bagID = 12, // End of oldTerms.
+
+ kRDFTerm_FirstCore = kRDFTerm_RDF,
+ kRDFTerm_LastCore = kRDFTerm_datatype,
+ kRDFTerm_FirstSyntax = kRDFTerm_FirstCore, // ! Yes, the syntax terms include the core terms.
+ kRDFTerm_LastSyntax = kRDFTerm_li,
+ kRDFTerm_FirstOld = kRDFTerm_aboutEach,
+ kRDFTerm_LastOld = kRDFTerm_bagID
+};
+
+enum {
+ kRDFMask_Other = 1 << kRDFTerm_Other,
+ kRDFMask_RDF = 1 << kRDFTerm_RDF,
+ kRDFMask_ID = 1 << kRDFTerm_ID,
+ kRDFMask_about = 1 << kRDFTerm_about,
+ kRDFMask_parseType = 1 << kRDFTerm_parseType,
+ kRDFMask_resource = 1 << kRDFTerm_resource,
+ kRDFMask_nodeID = 1 << kRDFTerm_nodeID,
+ kRDFMask_datatype = 1 << kRDFTerm_datatype,
+ kRDFMask_Description = 1 << kRDFTerm_Description,
+ kRDFMask_li = 1 << kRDFTerm_li,
+ kRDFMask_aboutEach = 1 << kRDFTerm_aboutEach,
+ kRDFMask_aboutEachPrefix = 1 << kRDFTerm_aboutEachPrefix,
+ kRDFMask_bagID = 1 << kRDFTerm_bagID
+};
+
+enum {
+ kRDF_HasValueElem = 0x10000000UL // ! Contains rdf:value child. Must fit within kXMP_ImplReservedMask!
+};
+
+// -------------------------------------------------------------------------------------------------
+// GetRDFTermKind
+// --------------
+
+static RDFTermKind
+GetRDFTermKind ( const XMP_VarString & name )
+{
+ RDFTermKind term = kRDFTerm_Other;
+
+ // Arranged to hopefully minimize the parse time for large XMP.
+
+ if ( (name.size() > 4) && (strncmp ( name.c_str(), "rdf:", 4 ) == 0) ) {
+
+ if ( name == "rdf:li" ) {
+ term = kRDFTerm_li;
+ } else if ( name == "rdf:parseType" ) {
+ term = kRDFTerm_parseType;
+ } else if ( name == "rdf:Description" ) {
+ term = kRDFTerm_Description;
+ } else if ( name == "rdf:about" ) {
+ term = kRDFTerm_about;
+ } else if ( name == "rdf:resource" ) {
+ term = kRDFTerm_resource;
+ } else if ( name == "rdf:RDF" ) {
+ term = kRDFTerm_RDF;
+ } else if ( name == "rdf:ID" ) {
+ term = kRDFTerm_ID;
+ } else if ( name == "rdf:nodeID" ) {
+ term = kRDFTerm_nodeID;
+ } else if ( name == "rdf:datatype" ) {
+ term = kRDFTerm_datatype;
+ } else if ( name == "rdf:aboutEach" ) {
+ term = kRDFTerm_aboutEach;
+ } else if ( name == "rdf:aboutEachPrefix" ) {
+ term = kRDFTerm_aboutEachPrefix;
+ } else if ( name == "rdf:bagID" ) {
+ term = kRDFTerm_bagID;
+ }
+
+ }
+
+ return term;
+
+} // GetRDFTermKind
+
+// =================================================================================================
+
+static void
+RemoveChild ( XMP_Node * xmpParent, size_t index )
+{
+ XMP_Node * child = xmpParent->children[index];
+ xmpParent->children.erase ( xmpParent->children.begin() + index );
+ delete child;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+static void
+RemoveQualifier ( XMP_Node * xmpParent, size_t index )
+{
+ XMP_Node * qualifier = xmpParent->qualifiers[index];
+ xmpParent->qualifiers.erase ( xmpParent->qualifiers.begin() + index );
+ delete qualifier;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+static void
+RemoveQualifier ( XMP_Node * xmpParent, XMP_NodePtrPos pos )
+{
+ XMP_Node * qualifier = *pos;
+ xmpParent->qualifiers.erase ( pos );
+ delete qualifier;
+}
+
+// =================================================================================================
+
+// -------------------------------------------------------------------------------------------------
+// IsCoreSyntaxTerm
+// ----------------
+//
+// 7.2.2 coreSyntaxTerms
+// rdf:RDF | rdf:ID | rdf:about | rdf:parseType | rdf:resource | rdf:nodeID | rdf:datatype
+
+static bool
+IsCoreSyntaxTerm ( RDFTermKind term )
+{
+ if ( (kRDFTerm_FirstCore <= term) && (term <= kRDFTerm_LastCore) ) return true;
+ return false;
+}
+
+// -------------------------------------------------------------------------------------------------
+// IsSyntaxTerm
+// ------------
+//
+// 7.2.3 syntaxTerms
+// coreSyntaxTerms | rdf:Description | rdf:li
+
+static bool
+IsSyntaxTerm ( RDFTermKind term )
+{
+ if ( (kRDFTerm_FirstSyntax <= term) && (term <= kRDFTerm_LastSyntax) ) return true;
+ return false;
+}
+
+// -------------------------------------------------------------------------------------------------
+// IsOldTerm
+// ---------
+//
+// 7.2.4 oldTerms
+// rdf:aboutEach | rdf:aboutEachPrefix | rdf:bagID
+
+static bool
+IsOldTerm ( RDFTermKind term )
+{
+ if ( (kRDFTerm_FirstOld <= term) && (term <= kRDFTerm_LastOld) ) return true;
+ return false;
+}
+
+// -------------------------------------------------------------------------------------------------
+// IsNodeElementName
+// -----------------
+//
+// 7.2.5 nodeElementURIs
+// anyURI - ( coreSyntaxTerms | rdf:li | oldTerms )
+
+static bool
+IsNodeElementName ( RDFTermKind term )
+{
+ if ( (term == kRDFTerm_li) || IsOldTerm ( term ) ) return false;
+ return (! IsCoreSyntaxTerm ( term ));
+}
+
+// -------------------------------------------------------------------------------------------------
+// IsPropertyElementName
+// ---------------------
+//
+// 7.2.6 propertyElementURIs
+// anyURI - ( coreSyntaxTerms | rdf:Description | oldTerms )
+
+static bool
+IsPropertyElementName ( RDFTermKind term )
+{
+ if ( (term == kRDFTerm_Description) || IsOldTerm ( term ) ) return false;
+ return (! IsCoreSyntaxTerm ( term ));
+}
+
+// -------------------------------------------------------------------------------------------------
+// IsPropertyAttributeName
+// -----------------------
+//
+// 7.2.7 propertyAttributeURIs
+// anyURI - ( coreSyntaxTerms | rdf:Description | rdf:li | oldTerms )
+
+static bool
+IsPropertyAttributeName ( RDFTermKind term )
+{
+ if ( (term == kRDFTerm_Description) || (term == kRDFTerm_li) || IsOldTerm ( term ) ) return false;
+ return (! IsCoreSyntaxTerm ( term ));
+}
+
+// -------------------------------------------------------------------------------------------------
+// IsNumberedArrayItemName
+// -----------------------
+//
+// Return true for a name of the form "rdf:_n", where n is a decimal integer. We're not strict about
+// the integer part, it just has to be characters in the range '0'..'9'.
+
+static bool
+IsNumberedArrayItemName ( const std::string & name )
+{
+ if ( name.size() <= 5 ) return false;
+ if ( strncmp ( name.c_str(), "rdf:_", 5 ) != 0 ) return false;
+ for ( size_t i = 5; i < name.size(); ++i ) {
+ if ( (name[i] < '0') | (name[i] > '9') ) return false;
+ }
+ return true;
+}
+
+// =================================================================================================
+// RDF_Parser::AddChildNode
+// ========================
+
+XMP_Node * RDF_Parser::AddChildNode ( XMP_Node * xmpParent, const XML_Node & xmlNode, const XMP_StringPtr value, bool isTopLevel )
+{
+
+ if ( xmlNode.ns.empty() ) {
+ XMP_Error error ( kXMPErr_BadRDF, "XML namespace required for all elements and attributes" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return 0;
+ }
+
+ bool isArrayParent = (xmpParent->options & kXMP_PropValueIsArray) !=0;
+ bool isArrayItem = (xmlNode.name == "rdf:li");
+ bool isValueNode = (xmlNode.name == "rdf:value");
+ XMP_OptionBits childOptions = 0;
+ XMP_StringPtr childName = xmlNode.name.c_str();
+
+ if ( isTopLevel ) {
+
+ // Lookup the schema node, adjust the XMP parent pointer.
+ XMP_Assert ( xmpParent->parent == 0 ); // Incoming parent must be the tree root.
+ XMP_Node * schemaNode = FindSchemaNode ( xmpParent, xmlNode.ns.c_str(), kXMP_CreateNodes );
+ if ( schemaNode->options & kXMP_NewImplicitNode ) schemaNode->options ^= kXMP_NewImplicitNode; // Clear the implicit node bit.
+ // *** Should use "opt &= ~flag" (no conditional), need runtime check for proper 32 bit code.
+ xmpParent = schemaNode;
+
+ // If this is an alias set the isAlias flag in the node and the hasAliases flag in the tree.
+ if ( sRegisteredAliasMap->find ( xmlNode.name ) != sRegisteredAliasMap->end() ) {
+ childOptions |= kXMP_PropIsAlias;
+ schemaNode->parent->options |= kXMP_PropHasAliases;
+ }
+
+ }
+
+ // Check use of rdf:li and rdf:_n names. Must be done before calling FindChildNode!
+ if ( isArrayItem ) {
+
+ // rdf:li can only be used for array children.
+ if ( ! isArrayParent ) {
+ XMP_Error error ( kXMPErr_BadRDF, "Misplaced rdf:li element" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return 0;
+ }
+ childName = kXMP_ArrayItemName;
+
+ } else if ( isArrayParent ) {
+
+ // Tolerate use of rdf:_n, don't verify order.
+ if ( IsNumberedArrayItemName ( xmlNode.name ) ) {
+ childName = kXMP_ArrayItemName;
+ isArrayItem = true;
+ } else {
+ XMP_Error error ( kXMPErr_BadRDF, "Array items cannot have arbitrary child names" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return 0;
+ }
+
+ }
+
+ // Make sure that this is not a duplicate of a named node.
+ if ( ! (isArrayItem | isValueNode) ) {
+ if ( FindChildNode ( xmpParent, childName, kXMP_ExistingOnly ) != 0 ) {
+ XMP_Error error ( kXMPErr_BadXMP, "Duplicate property or field node" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return 0;
+ }
+ }
+
+ // Make sure an rdf:value node is used properly.
+ if ( isValueNode ) {
+ if ( isTopLevel || (! (xmpParent->options & kXMP_PropValueIsStruct)) ) {
+ XMP_Error error ( kXMPErr_BadRDF, "Misplaced rdf:value element" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return 0;
+ }
+ xmpParent->options |= kRDF_HasValueElem;
+ }
+
+ // Add the new child to the XMP parent node.
+ XMP_Node * newChild = new XMP_Node ( xmpParent, childName, value, childOptions );
+ if ( (! isValueNode) || xmpParent->children.empty() ) {
+ xmpParent->children.push_back ( newChild );
+ } else {
+ xmpParent->children.insert ( xmpParent->children.begin(), newChild );
+ }
+
+ return newChild;
+
+} // RDF_Parser::AddChildNode
+
+// =================================================================================================
+// RDF_Parser::AddQualifierNode
+// ============================
+
+XMP_Node * RDF_Parser::AddQualifierNode ( XMP_Node * xmpParent, const XMP_VarString & name, const XMP_VarString & value )
+{
+
+ const bool isLang = (name == "xml:lang");
+ const bool isType = (name == "rdf:type");
+
+ XMP_Node * newQual = 0;
+
+ newQual = new XMP_Node ( xmpParent, name, value, kXMP_PropIsQualifier );
+
+ if ( ! (isLang | isType) ) {
+ xmpParent->qualifiers.push_back ( newQual );
+ } else if ( isLang ) {
+ if ( xmpParent->qualifiers.empty() ) {
+ xmpParent->qualifiers.push_back ( newQual );
+ } else {
+ xmpParent->qualifiers.insert ( xmpParent->qualifiers.begin(), newQual );
+ }
+ xmpParent->options |= kXMP_PropHasLang;
+ } else {
+ XMP_Assert ( isType );
+ if ( xmpParent->qualifiers.empty() ) {
+ xmpParent->qualifiers.push_back ( newQual );
+ } else {
+ size_t offset = 0;
+ if ( XMP_PropHasLang ( xmpParent->options ) ) offset = 1;
+ xmpParent->qualifiers.insert ( xmpParent->qualifiers.begin()+offset, newQual );
+ }
+ xmpParent->options |= kXMP_PropHasType;
+ }
+
+ xmpParent->options |= kXMP_PropHasQualifiers;
+
+ return newQual;
+
+} // RDF_Parser::AddQualifierNode
+
+// =================================================================================================
+// RDF_Parser::AddQualifierNode
+// ============================
+
+XMP_Node * RDF_Parser::AddQualifierNode ( XMP_Node * xmpParent, const XML_Node & attr )
+{
+ if ( attr.ns.empty() ) {
+ XMP_Error error ( kXMPErr_BadRDF, "XML namespace required for all elements and attributes" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return 0;
+ }
+
+ return this->AddQualifierNode ( xmpParent, attr.name, attr.value );
+
+} // RDF_Parser::AddQualifierNode
+
+// =================================================================================================
+// RDF_Parser::FixupQualifiedNode
+// ==============================
+//
+// The parent is an RDF pseudo-struct containing an rdf:value field. Fix the XMP data model. The
+// rdf:value node must be the first child, the other children are qualifiers. The form, value, and
+// children of the rdf:value node are the real ones. The rdf:value node's qualifiers must be added
+// to the others.
+
+void RDF_Parser::FixupQualifiedNode ( XMP_Node * xmpParent )
+{
+ size_t qualNum, qualLim;
+ size_t childNum, childLim;
+
+ XMP_Enforce ( (xmpParent->options & kXMP_PropValueIsStruct) && (! xmpParent->children.empty()) );
+
+ XMP_Node * valueNode = xmpParent->children[0];
+ XMP_Enforce ( valueNode->name == "rdf:value" );
+
+ xmpParent->qualifiers.reserve ( xmpParent->qualifiers.size() + xmpParent->children.size() + valueNode->qualifiers.size() );
+
+ // Move the qualifiers on the value node to the parent. Make sure an xml:lang qualifier stays at
+ // the front.
+
+ qualNum = 0;
+ qualLim = valueNode->qualifiers.size();
+
+ if ( valueNode->options & kXMP_PropHasLang ) {
+
+ if ( xmpParent->options & kXMP_PropHasLang ) {
+ XMP_Error error ( kXMPErr_BadXMP, "Duplicate xml:lang for rdf:value element" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ XMP_Assert ( xmpParent->qualifiers[0]->name == "xml:lang" );
+ RemoveQualifier ( xmpParent, 0 ); // Use the rdf:value node's language.
+ }
+
+ XMP_Node * langQual = valueNode->qualifiers[0];
+
+ XMP_Assert ( langQual->name == "xml:lang" );
+ langQual->parent = xmpParent;
+ xmpParent->options |= kXMP_PropHasLang;
+ XMP_ClearOption ( valueNode->options, kXMP_PropHasLang );
+
+ if ( xmpParent->qualifiers.empty() ) {
+ xmpParent->qualifiers.push_back ( langQual ); // *** Should use utilities to add qual & set parent.
+ } else {
+ xmpParent->qualifiers.insert ( xmpParent->qualifiers.begin(), langQual );
+ }
+ valueNode->qualifiers[0] = 0; // We just moved it to the parent.
+
+ qualNum = 1; // Start the remaining copy after the xml:lang qualifier.
+
+ }
+
+ for ( ; qualNum != qualLim; ++qualNum ) {
+
+ XMP_Node * currQual = valueNode->qualifiers[qualNum];
+ XMP_NodePtrPos existingPos;
+ XMP_Node * existingQual = FindQualifierNode ( xmpParent, currQual->name.c_str(), kXMP_ExistingOnly, &existingPos );
+
+ if ( existingQual != 0 ) {
+ XMP_Error error ( kXMPErr_BadXMP, "Duplicate qualifier node" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ RemoveQualifier ( xmpParent, existingPos ); // Use the rdf:value node's qualifier.
+ }
+
+ currQual->parent = xmpParent;
+ xmpParent->qualifiers.push_back ( currQual );
+ valueNode->qualifiers[qualNum] = 0; // We just moved it to the parent.
+
+ }
+
+ valueNode->qualifiers.clear(); // ! There should be nothing but null pointers.
+
+ // Change the parent's other children into qualifiers. This loop starts at 1, child 0 is the
+ // rdf:value node. Put xml:lang at the front, append all others.
+
+ for ( childNum = 1, childLim = xmpParent->children.size(); childNum != childLim; ++childNum ) {
+
+ XMP_Node * currQual = xmpParent->children[childNum];
+ bool isLang = (currQual->name == "xml:lang");
+
+ if ( FindQualifierNode ( xmpParent, currQual->name.c_str(), kXMP_ExistingOnly ) != 0 ) {
+ XMP_Error error ( kXMPErr_BadXMP, "Duplicate qualifier" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ delete currQual;
+
+ } else {
+
+ currQual->options |= kXMP_PropIsQualifier;
+ currQual->parent = xmpParent;
+
+ if ( isLang ) {
+ xmpParent->options |= kXMP_PropHasLang;
+ } else if ( currQual->name == "rdf:type" ) {
+ xmpParent->options |= kXMP_PropHasType;
+ }
+
+ if ( (! isLang) || xmpParent->qualifiers.empty() ) {
+ xmpParent->qualifiers.push_back ( currQual );
+ } else {
+ xmpParent->qualifiers.insert ( xmpParent->qualifiers.begin(), currQual );
+ }
+
+ }
+
+ xmpParent->children[childNum] = 0; // We just moved it to the qualifers, or ignored it.
+
+ }
+
+ if ( ! xmpParent->qualifiers.empty() ) xmpParent->options |= kXMP_PropHasQualifiers;
+
+ // Move the options and value last, other checks need the parent's original options. Move the
+ // value node's children to be the parent's children. Delete the now useless value node.
+
+ XMP_Assert ( xmpParent->options & (kXMP_PropValueIsStruct | kRDF_HasValueElem) );
+ xmpParent->options &= ~ (kXMP_PropValueIsStruct | kRDF_HasValueElem);
+ xmpParent->options |= valueNode->options;
+
+ xmpParent->value.swap ( valueNode->value );
+
+ xmpParent->children[0] = 0; // ! Remove the value node itself before the swap.
+ xmpParent->children.swap ( valueNode->children );
+
+ for ( childNum = 0, childLim = xmpParent->children.size(); childNum != childLim; ++childNum ) {
+ XMP_Node * currChild = xmpParent->children[childNum];
+ currChild->parent = xmpParent;
+ }
+
+ delete valueNode;
+
+} // RDF_Parser::FixupQualifiedNode
+
+// =================================================================================================
+// RDF_Parser::RDF
+// ===============
+//
+// 7.2.9 RDF
+// start-element ( URI == rdf:RDF, attributes == set() )
+// nodeElementList
+// end-element()
+//
+// The top level rdf:RDF node. It can only have xmlns attributes, which have already been removed
+// during construction of the XML tree.
+
+void RDF_Parser::RDF ( XMP_Node * xmpTree, const XML_Node & xmlNode )
+{
+
+ if ( ! xmlNode.attrs.empty() ) {
+ XMP_Error error ( kXMPErr_BadRDF, "Invalid attributes of rdf:RDF element" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ }
+ this->NodeElementList ( xmpTree, xmlNode, kIsTopLevel ); // ! Attributes are ignored.
+
+} // RDF_Parser::RDF
+
+// =================================================================================================
+// RDF_Parser::NodeElementList
+// ===========================
+//
+// 7.2.10 nodeElementList
+// ws* ( nodeElement ws* )*
+
+void RDF_Parser::NodeElementList ( XMP_Node * xmpParent, const XML_Node & xmlParent, bool isTopLevel )
+{
+ XMP_Assert ( isTopLevel );
+
+ XML_cNodePos currChild = xmlParent.content.begin(); // *** Change these loops to the indexed pattern.
+ XML_cNodePos endChild = xmlParent.content.end();
+
+ for ( ; currChild != endChild; ++currChild ) {
+ if ( (*currChild)->IsWhitespaceNode() ) continue;
+ this->NodeElement ( xmpParent, **currChild, isTopLevel );
+ }
+
+} // RDF_Parser::NodeElementList
+
+// =================================================================================================
+// RDF_Parser::NodeElement
+// =======================
+//
+// 7.2.5 nodeElementURIs
+// anyURI - ( coreSyntaxTerms | rdf:li | oldTerms )
+//
+// 7.2.11 nodeElement
+// start-element ( URI == nodeElementURIs,
+// attributes == set ( ( idAttr | nodeIdAttr | aboutAttr )?, propertyAttr* ) )
+// propertyEltList
+// end-element()
+//
+// A node element URI is rdf:Description or anything else that is not an RDF term.
+
+void RDF_Parser::NodeElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
+{
+ RDFTermKind nodeTerm = GetRDFTermKind ( xmlNode.name );
+ if ( (nodeTerm != kRDFTerm_Description) && (nodeTerm != kRDFTerm_Other) ) {
+ XMP_Error error ( kXMPErr_BadRDF, "Node element must be rdf:Description or typedNode" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ } else if ( isTopLevel && (nodeTerm == kRDFTerm_Other) ) {
+ XMP_Error error ( kXMPErr_BadXMP, "Top level typedNode not allowed" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ } else {
+ this->NodeElementAttrs ( xmpParent, xmlNode, isTopLevel );
+ this->PropertyElementList ( xmpParent, xmlNode, isTopLevel );
+ }
+
+} // RDF_Parser::NodeElement
+
+// =================================================================================================
+// RDF_Parser::NodeElementAttrs
+// ============================
+//
+// 7.2.7 propertyAttributeURIs
+// anyURI - ( coreSyntaxTerms | rdf:Description | rdf:li | oldTerms )
+//
+// 7.2.11 nodeElement
+// start-element ( URI == nodeElementURIs,
+// attributes == set ( ( idAttr | nodeIdAttr | aboutAttr )?, propertyAttr* ) )
+// propertyEltList
+// end-element()
+//
+// Process the attribute list for an RDF node element. A property attribute URI is anything other
+// than an RDF term. The rdf:ID and rdf:nodeID attributes are simply ignored, as are rdf:about
+// attributes on inner nodes.
+
+static const XMP_OptionBits kExclusiveAttrMask = (kRDFMask_ID | kRDFMask_nodeID | kRDFMask_about);
+
+void RDF_Parser::NodeElementAttrs ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
+{
+ XMP_OptionBits exclusiveAttrs = 0; // Used to detect attributes that are mutually exclusive.
+
+ XML_cNodePos currAttr = xmlNode.attrs.begin();
+ XML_cNodePos endAttr = xmlNode.attrs.end();
+
+ for ( ; currAttr != endAttr; ++currAttr ) {
+
+ RDFTermKind attrTerm = GetRDFTermKind ( (*currAttr)->name );
+
+ switch ( attrTerm ) {
+
+ case kRDFTerm_ID :
+ case kRDFTerm_nodeID :
+ case kRDFTerm_about :
+
+ if ( exclusiveAttrs & kExclusiveAttrMask ) {
+ XMP_Error error ( kXMPErr_BadRDF, "Mutally exclusive about, ID, nodeID attributes" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ continue; // Skip the later mutually exclusive attributes.
+ }
+ exclusiveAttrs |= (1 << attrTerm);
+
+ if ( isTopLevel && (attrTerm == kRDFTerm_about) ) {
+ // This is the rdf:about attribute on a top level node. Set the XMP tree name if
+ // it doesn't have a name yet. Make sure this name matches the XMP tree name.
+ XMP_Assert ( xmpParent->parent == 0 ); // Must be the tree root node.
+ if ( xmpParent->name.empty() ) {
+ xmpParent->name = (*currAttr)->value;
+ } else if ( ! (*currAttr)->value.empty() ) {
+ if ( xmpParent->name != (*currAttr)->value ) {
+ XMP_Error error ( kXMPErr_BadXMP, "Mismatched top level rdf:about values" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ }
+ }
+ }
+
+ break;
+
+ case kRDFTerm_Other :
+ this->AddChildNode ( xmpParent, **currAttr, (*currAttr)->value.c_str(), isTopLevel );
+ break;
+
+ default :
+ {
+ XMP_Error error ( kXMPErr_BadRDF, "Invalid nodeElement attribute" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ }
+ continue;
+
+ }
+
+ }
+
+} // RDF_Parser::NodeElementAttrs
+
+// =================================================================================================
+// RDF_Parser::PropertyElementList
+// ===============================
+//
+// 7.2.13 propertyEltList
+// ws* ( propertyElt ws* )*
+
+void RDF_Parser::PropertyElementList ( XMP_Node * xmpParent, const XML_Node & xmlParent, bool isTopLevel )
+{
+ XML_cNodePos currChild = xmlParent.content.begin();
+ XML_cNodePos endChild = xmlParent.content.end();
+
+ for ( ; currChild != endChild; ++currChild ) {
+ if ( (*currChild)->IsWhitespaceNode() ) continue;
+ if ( (*currChild)->kind != kElemNode ) {
+ XMP_Error error ( kXMPErr_BadRDF, "Expected property element node not found" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ continue;
+ }
+ this->PropertyElement ( xmpParent, **currChild, isTopLevel );
+ }
+
+} // RDF_Parser::PropertyElementList
+
+// =================================================================================================
+// RDF_Parser::PropertyElement
+// ===========================
+//
+// 7.2.14 propertyElt
+// resourcePropertyElt | literalPropertyElt | parseTypeLiteralPropertyElt |
+// parseTypeResourcePropertyElt | parseTypeCollectionPropertyElt | parseTypeOtherPropertyElt | emptyPropertyElt
+//
+// 7.2.15 resourcePropertyElt
+// start-element ( URI == propertyElementURIs, attributes == set ( idAttr? ) )
+// ws* nodeElement ws*
+// end-element()
+//
+// 7.2.16 literalPropertyElt
+// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, datatypeAttr?) )
+// text()
+// end-element()
+//
+// 7.2.17 parseTypeLiteralPropertyElt
+// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseLiteral ) )
+// literal
+// end-element()
+//
+// 7.2.18 parseTypeResourcePropertyElt
+// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseResource ) )
+// propertyEltList
+// end-element()
+//
+// 7.2.19 parseTypeCollectionPropertyElt
+// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseCollection ) )
+// nodeElementList
+// end-element()
+//
+// 7.2.20 parseTypeOtherPropertyElt
+// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseOther ) )
+// propertyEltList
+// end-element()
+//
+// 7.2.21 emptyPropertyElt
+// start-element ( URI == propertyElementURIs,
+// attributes == set ( idAttr?, ( resourceAttr | nodeIdAttr )?, propertyAttr* ) )
+// end-element()
+//
+// The various property element forms are not distinguished by the XML element name, but by their
+// attributes for the most part. The exceptions are resourcePropertyElt and literalPropertyElt. They
+// are distinguished by their XML element content.
+//
+// NOTE: The RDF syntax does not explicitly include the xml:lang attribute although it can appear in
+// many of these. We have to allow for it in the attibute counts below.
+
+void RDF_Parser::PropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
+{
+ RDFTermKind nodeTerm = GetRDFTermKind ( xmlNode.name );
+ if ( ! IsPropertyElementName ( nodeTerm ) ) {
+ XMP_Error error ( kXMPErr_BadRDF, "Invalid property element name" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return;
+ }
+
+ if ( xmlNode.attrs.size() > 3 ) {
+
+ // Only an emptyPropertyElt can have more than 3 attributes.
+ this->EmptyPropertyElement ( xmpParent, xmlNode, isTopLevel );
+
+ } else {
+
+ // Look through the attributes for one that isn't rdf:ID or xml:lang, it will usually tell
+ // what we should be dealing with. The called routines must verify their specific syntax!
+
+ XML_cNodePos currAttr = xmlNode.attrs.begin();
+ XML_cNodePos endAttr = xmlNode.attrs.end();
+ XMP_VarString * attrName = 0;
+
+ for ( ; currAttr != endAttr; ++currAttr ) {
+ attrName = &((*currAttr)->name);
+ if ( (*attrName != "xml:lang") && (*attrName != "rdf:ID") ) break;
+ }
+
+ if ( currAttr != endAttr ) {
+
+ XMP_Assert ( attrName != 0 );
+ XMP_VarString& attrValue = (*currAttr)->value;
+
+ if ( *attrName == "rdf:datatype" ) {
+ this->LiteralPropertyElement ( xmpParent, xmlNode, isTopLevel );
+ } else if ( *attrName != "rdf:parseType" ) {
+ this->EmptyPropertyElement ( xmpParent, xmlNode, isTopLevel );
+ } else if ( attrValue == "Literal" ) {
+ this->ParseTypeLiteralPropertyElement ( xmpParent, xmlNode, isTopLevel );
+ } else if ( attrValue == "Resource" ) {
+ this->ParseTypeResourcePropertyElement ( xmpParent, xmlNode, isTopLevel );
+ } else if ( attrValue == "Collection" ) {
+ this->ParseTypeCollectionPropertyElement ( xmpParent, xmlNode, isTopLevel );
+ } else {
+ this->ParseTypeOtherPropertyElement ( xmpParent, xmlNode, isTopLevel );
+ }
+
+ } else {
+
+ // Only rdf:ID and xml:lang, could be a resourcePropertyElt, a literalPropertyElt, or an.
+ // emptyPropertyElt. Look at the child XML nodes to decide which.
+
+ if ( xmlNode.content.empty() ) {
+
+ this->EmptyPropertyElement ( xmpParent, xmlNode, isTopLevel );
+
+ } else {
+
+ XML_cNodePos currChild = xmlNode.content.begin();
+ XML_cNodePos endChild = xmlNode.content.end();
+
+ for ( ; currChild != endChild; ++currChild ) {
+ if ( (*currChild)->kind != kCDataNode ) break;
+ }
+
+ if ( currChild == endChild ) {
+ this->LiteralPropertyElement ( xmpParent, xmlNode, isTopLevel );
+ } else {
+ this->ResourcePropertyElement ( xmpParent, xmlNode, isTopLevel );
+ }
+
+ }
+
+ }
+
+ }
+
+} // RDF_Parser::PropertyElement
+
+// =================================================================================================
+// RDF_Parser::ResourcePropertyElement
+// ===================================
+//
+// 7.2.15 resourcePropertyElt
+// start-element ( URI == propertyElementURIs, attributes == set ( idAttr? ) )
+// ws* nodeElement ws*
+// end-element()
+//
+// This handles structs using an rdf:Description node, arrays using rdf:Bag/Seq/Alt, and Typed Nodes.
+// It also catches and cleans up qualified properties written with rdf:Description and rdf:value.
+
+void RDF_Parser::ResourcePropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
+{
+ if ( isTopLevel && (xmlNode.name == "iX:changes") ) return; // Strip old "punchcard" chaff.
+
+ XMP_Node * newCompound = this->AddChildNode ( xmpParent, xmlNode, "", isTopLevel );
+ if ( newCompound == 0 ) return; // Ignore lower level errors.
+
+ XML_cNodePos currAttr = xmlNode.attrs.begin();
+ XML_cNodePos endAttr = xmlNode.attrs.end();
+
+ for ( ; currAttr != endAttr; ++currAttr ) {
+ XMP_VarString & attrName = (*currAttr)->name;
+ if ( attrName == "xml:lang" ) {
+ this->AddQualifierNode ( newCompound, **currAttr );
+ } else if ( attrName == "rdf:ID" ) {
+ continue; // Ignore all rdf:ID attributes.
+ } else {
+ XMP_Error error ( kXMPErr_BadRDF, "Invalid attribute for resource property element" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ continue;
+ }
+ }
+
+ XML_cNodePos currChild = xmlNode.content.begin();
+ XML_cNodePos endChild = xmlNode.content.end();
+
+ for ( ; currChild != endChild; ++currChild ) {
+ if ( ! (*currChild)->IsWhitespaceNode() ) break;
+ }
+ if ( currChild == endChild ) {
+ XMP_Error error ( kXMPErr_BadRDF, "Missing child of resource property element" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return;
+ }
+ if ( (*currChild)->kind != kElemNode ) {
+ XMP_Error error ( kXMPErr_BadRDF, "Children of resource property element must be XML elements" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return;
+ }
+
+ if ( (*currChild)->name == "rdf:Bag" ) {
+ newCompound->options |= kXMP_PropValueIsArray;
+ } else if ( (*currChild)->name == "rdf:Seq" ) {
+ newCompound->options |= kXMP_PropValueIsArray | kXMP_PropArrayIsOrdered;
+ } else if ( (*currChild)->name == "rdf:Alt" ) {
+ newCompound->options |= kXMP_PropValueIsArray | kXMP_PropArrayIsOrdered | kXMP_PropArrayIsAlternate;
+ } else {
+ // This is the Typed Node case. Add an rdf:type qualifier with a URI value.
+ if ( (*currChild)->name != "rdf:Description" ) {
+ XMP_VarString typeName ( (*currChild)->ns );
+ size_t colonPos = (*currChild)->name.find_first_of(':');
+ if ( colonPos == XMP_VarString::npos ) {
+ XMP_Error error ( kXMPErr_BadXMP, "All XML elements must be in a namespace" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return;
+ }
+ typeName.append ( (*currChild)->name, colonPos+1, XMP_VarString::npos ); // Append just the local name.
+ XMP_Node * typeQual = this->AddQualifierNode ( newCompound, XMP_VarString("rdf:type"), typeName );
+ if ( typeQual != 0 ) typeQual->options |= kXMP_PropValueIsURI;
+ }
+ newCompound->options |= kXMP_PropValueIsStruct;
+ }
+
+ this->NodeElement ( newCompound, **currChild, kNotTopLevel );
+ if ( newCompound->options & kRDF_HasValueElem ) {
+ this->FixupQualifiedNode ( newCompound );
+ } else if ( newCompound->options & kXMP_PropArrayIsAlternate ) {
+ DetectAltText ( newCompound );
+ }
+
+ for ( ++currChild; currChild != endChild; ++currChild ) {
+ if ( ! (*currChild)->IsWhitespaceNode() ) {
+ XMP_Error error ( kXMPErr_BadRDF, "Invalid child of resource property element" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ break; // Don't bother looking for more trailing errors.
+ }
+ }
+
+} // RDF_Parser::ResourcePropertyElement
+
+// =================================================================================================
+// RDF_Parser::LiteralPropertyElement
+// ==================================
+//
+// 7.2.16 literalPropertyElt
+// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, datatypeAttr?) )
+// text()
+// end-element()
+//
+// Add a leaf node with the text value and qualifiers for the attributes.
+
+void RDF_Parser::LiteralPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
+{
+ XMP_Node * newChild = this->AddChildNode ( xmpParent, xmlNode, "", isTopLevel );
+ if ( newChild == 0 ) return; // Ignore lower level errors.
+
+ XML_cNodePos currAttr = xmlNode.attrs.begin();
+ XML_cNodePos endAttr = xmlNode.attrs.end();
+
+ for ( ; currAttr != endAttr; ++currAttr ) {
+ XMP_VarString & attrName = (*currAttr)->name;
+ if ( attrName == "xml:lang" ) {
+ this->AddQualifierNode ( newChild, **currAttr );
+ } else if ( (attrName == "rdf:ID") || (attrName == "rdf:datatype") ) {
+ continue; // Ignore all rdf:ID and rdf:datatype attributes.
+ } else {
+ XMP_Error error ( kXMPErr_BadRDF, "Invalid attribute for literal property element" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ continue;
+ }
+ }
+
+ XML_cNodePos currChild = xmlNode.content.begin();
+ XML_cNodePos endChild = xmlNode.content.end();
+ size_t textSize = 0;
+
+ for ( ; currChild != endChild; ++currChild ) {
+ if ( (*currChild)->kind == kCDataNode ) {
+ textSize += (*currChild)->value.size();
+ } else {
+ XMP_Error error ( kXMPErr_BadRDF, "Invalid child of literal property element" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ }
+ }
+
+ newChild->value.reserve ( textSize );
+
+ for ( currChild = xmlNode.content.begin(); currChild != endChild; ++currChild ) {
+ newChild->value += (*currChild)->value;
+ }
+
+} // RDF_Parser::LiteralPropertyElement
+
+// =================================================================================================
+// RDF_Parser::ParseTypeLiteralPropertyElement
+// ===========================================
+//
+// 7.2.17 parseTypeLiteralPropertyElt
+// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseLiteral ) )
+// literal
+// end-element()
+
+void RDF_Parser::ParseTypeLiteralPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
+{
+ IgnoreParam(xmpParent); IgnoreParam(xmlNode); IgnoreParam(isTopLevel);
+ XMP_Error error ( kXMPErr_BadXMP, "ParseTypeLiteral property element not allowed" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+
+} // RDF_Parser::ParseTypeLiteralPropertyElement
+
+// =================================================================================================
+// RDF_Parser::ParseTypeResourcePropertyElement
+// ============================================
+//
+// 7.2.18 parseTypeResourcePropertyElt
+// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseResource ) )
+// propertyEltList
+// end-element()
+//
+// Add a new struct node with a qualifier for the possible rdf:ID attribute. Then process the XML
+// child nodes to get the struct fields.
+
+void RDF_Parser::ParseTypeResourcePropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
+{
+ XMP_Node * newStruct = this->AddChildNode ( xmpParent, xmlNode, "", isTopLevel );
+ if ( newStruct == 0 ) return; // Ignore lower level errors.
+ newStruct->options |= kXMP_PropValueIsStruct;
+
+ XML_cNodePos currAttr = xmlNode.attrs.begin();
+ XML_cNodePos endAttr = xmlNode.attrs.end();
+
+ for ( ; currAttr != endAttr; ++currAttr ) {
+ XMP_VarString & attrName = (*currAttr)->name;
+ if ( attrName == "rdf:parseType" ) {
+ continue; // ! The caller ensured the value is "Resource".
+ } else if ( attrName == "xml:lang" ) {
+ this->AddQualifierNode ( newStruct, **currAttr );
+ } else if ( attrName == "rdf:ID" ) {
+ continue; // Ignore all rdf:ID attributes.
+ } else {
+ XMP_Error error ( kXMPErr_BadRDF, "Invalid attribute for ParseTypeResource property element" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ continue;
+ }
+ }
+
+ this->PropertyElementList ( newStruct, xmlNode, kNotTopLevel );
+
+ if ( newStruct->options & kRDF_HasValueElem ) this->FixupQualifiedNode ( newStruct );
+
+ // *** Need to look for arrays using rdf:Description and rdf:type.
+
+} // RDF_Parser::ParseTypeResourcePropertyElement
+
+// =================================================================================================
+// RDF_Parser::ParseTypeCollectionPropertyElement
+// ==============================================
+//
+// 7.2.19 parseTypeCollectionPropertyElt
+// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseCollection ) )
+// nodeElementList
+// end-element()
+
+void RDF_Parser::ParseTypeCollectionPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
+{
+ IgnoreParam(xmpParent); IgnoreParam(xmlNode); IgnoreParam(isTopLevel);
+ XMP_Error error ( kXMPErr_BadXMP, "ParseTypeCollection property element not allowed" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+
+} // RDF_Parser::ParseTypeCollectionPropertyElement
+
+// =================================================================================================
+// RDF_Parser::ParseTypeOtherPropertyElement
+// =========================================
+//
+// 7.2.20 parseTypeOtherPropertyElt
+// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseOther ) )
+// propertyEltList
+// end-element()
+
+void RDF_Parser::ParseTypeOtherPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
+{
+ IgnoreParam(xmpParent); IgnoreParam(xmlNode); IgnoreParam(isTopLevel);
+ XMP_Error error ( kXMPErr_BadXMP, "ParseTypeOther property element not allowed" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+
+} // RDF_Parser::ParseTypeOtherPropertyElement
+
+// =================================================================================================
+// RDF_Parser::EmptyPropertyElement
+// ================================
+//
+// 7.2.21 emptyPropertyElt
+// start-element ( URI == propertyElementURIs,
+// attributes == set ( idAttr?, ( resourceAttr | nodeIdAttr )?, propertyAttr* ) )
+// end-element()
+//
+// <ns:Prop1/> <!-- a simple property with an empty value -->
+// <ns:Prop2 rdf:resource="http://www.adobe.com/"/> <!-- a URI value -->
+// <ns:Prop3 rdf:value="..." ns:Qual="..."/> <!-- a simple qualified property -->
+// <ns:Prop4 ns:Field1="..." ns:Field2="..."/> <!-- a struct with simple fields -->
+//
+// An emptyPropertyElt is an element with no contained content, just a possibly empty set of
+// attributes. An emptyPropertyElt can represent three special cases of simple XMP properties: a
+// simple property with an empty value (ns:Prop1), a simple property whose value is a URI
+// (ns:Prop2), or a simple property with simple qualifiers (ns:Prop3). An emptyPropertyElt can also
+// represent an XMP struct whose fields are all simple and unqualified (ns:Prop4).
+//
+// It is an error to use both rdf:value and rdf:resource - that can lead to invalid RDF in the
+// verbose form written using a literalPropertyElt.
+//
+// The XMP mapping for an emptyPropertyElt is a bit different from generic RDF, partly for
+// design reasons and partly for historical reasons. The XMP mapping rules are:
+// 1. If there is an rdf:value attribute then this is a simple property with a text value.
+// All other attributes are qualifiers.
+// 2. If there is an rdf:resource attribute then this is a simple property with a URI value.
+// All other attributes are qualifiers.
+// 3. If there are no attributes other than xml:lang, rdf:ID, or rdf:nodeID then this is a simple
+// property with an empty value.
+// 4. Otherwise this is a struct, the attributes other than xml:lang, rdf:ID, or rdf:nodeID are fields.
+
+void RDF_Parser::EmptyPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
+{
+ bool hasPropertyAttrs = false;
+ bool hasResourceAttr = false;
+ bool hasNodeIDAttr = false;
+ bool hasValueAttr = false;
+
+ const XML_Node * valueNode = 0; // ! Can come from rdf:value or rdf:resource.
+
+ if ( ! xmlNode.content.empty() ) {
+ XMP_Error error ( kXMPErr_BadRDF, "Nested content not allowed with rdf:resource or property attributes" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return;
+ }
+
+ // First figure out what XMP this maps to and remember the XML node for a simple value.
+
+ XML_cNodePos currAttr = xmlNode.attrs.begin();
+ XML_cNodePos endAttr = xmlNode.attrs.end();
+
+ for ( ; currAttr != endAttr; ++currAttr ) {
+
+ RDFTermKind attrTerm = GetRDFTermKind ( (*currAttr)->name );
+
+ switch ( attrTerm ) {
+
+ case kRDFTerm_ID :
+ // Nothing to do.
+ break;
+
+ case kRDFTerm_resource :
+ if ( hasNodeIDAttr ) {
+ XMP_Error error ( kXMPErr_BadRDF, "Empty property element can't have both rdf:resource and rdf:nodeID" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return;
+ }
+ if ( hasValueAttr ) {
+ XMP_Error error ( kXMPErr_BadXMP, "Empty property element can't have both rdf:value and rdf:resource" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return;
+ }
+ hasResourceAttr = true;
+ if ( ! hasValueAttr ) valueNode = *currAttr;
+ break;
+
+ case kRDFTerm_nodeID :
+ if ( hasResourceAttr ) {
+ XMP_Error error ( kXMPErr_BadRDF, "Empty property element can't have both rdf:resource and rdf:nodeID" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return;
+ }
+ hasNodeIDAttr = true;
+ break;
+
+ case kRDFTerm_Other :
+ if ( (*currAttr)->name == "rdf:value" ) {
+ if ( hasResourceAttr ) {
+ XMP_Error error ( kXMPErr_BadXMP, "Empty property element can't have both rdf:value and rdf:resource" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return;
+ }
+ hasValueAttr = true;
+ valueNode = *currAttr;
+ } else if ( (*currAttr)->name != "xml:lang" ) {
+ hasPropertyAttrs = true;
+ }
+ break;
+
+ default :
+ {
+ XMP_Error error ( kXMPErr_BadRDF, "Unrecognized attribute of empty property element" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ }
+
+ return;
+
+ }
+
+ }
+
+ // Create the right kind of child node and visit the attributes again to add the fields or qualifiers.
+ // ! Because of implementation vagaries, the xmpParent is the tree root for top level properties.
+ // ! The schema is found, created if necessary, by AddChildNode.
+
+ XMP_Node * childNode = this->AddChildNode ( xmpParent, xmlNode, "", isTopLevel );
+ if ( childNode == 0 ) return; // Ignore lower level errors.
+ bool childIsStruct = false;
+
+ if ( hasValueAttr | hasResourceAttr ) {
+ childNode->value = valueNode->value;
+ if ( ! hasValueAttr ) childNode->options |= kXMP_PropValueIsURI; // ! Might have both rdf:value and rdf:resource.
+ } else if ( hasPropertyAttrs ) {
+ childNode->options |= kXMP_PropValueIsStruct;
+ childIsStruct = true;
+ }
+
+ currAttr = xmlNode.attrs.begin();
+ endAttr = xmlNode.attrs.end();
+
+ for ( ; currAttr != endAttr; ++currAttr ) {
+
+ if ( *currAttr == valueNode ) continue; // Skip the rdf:value or rdf:resource attribute holding the value.
+ RDFTermKind attrTerm = GetRDFTermKind ( (*currAttr)->name );
+
+ switch ( attrTerm ) {
+
+ case kRDFTerm_ID :
+ case kRDFTerm_nodeID :
+ break; // Ignore all rdf:ID and rdf:nodeID attributes.
+
+ case kRDFTerm_resource :
+ this->AddQualifierNode ( childNode, **currAttr );
+ break;
+
+ case kRDFTerm_Other :
+ if ( (! childIsStruct) || (*currAttr)->name == "xml:lang" ) {
+ this->AddQualifierNode ( childNode, **currAttr );
+ } else {
+ this->AddChildNode ( childNode, **currAttr, (*currAttr)->value.c_str(), false );
+ }
+ break;
+
+ default :
+ {
+ XMP_Error error ( kXMPErr_BadRDF, "Unrecognized attribute of empty property element" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ }
+ continue;
+
+ }
+
+ }
+
+} // RDF_Parser::EmptyPropertyElement
+
+// =================================================================================================
+// XMPMeta::ProcessRDF
+// ===================
+//
+// Parse the XML tree of the RDF and build the corresponding XMP tree.
+
+void XMPMeta::ProcessRDF ( const XML_Node & rdfNode, XMP_OptionBits options )
+{
+ IgnoreParam(options);
+
+ RDF_Parser parser ( &this->errorCallback );
+
+ parser.RDF ( &this->tree, rdfNode );
+
+} // XMPMeta::ProcessRDF
+
+// =================================================================================================
diff --git a/gpr/source/lib/xmp_core/UnicodeConversions.cpp b/gpr/source/lib/xmp_core/UnicodeConversions.cpp
new file mode 100644
index 0000000..f9863cd
--- /dev/null
+++ b/gpr/source/lib/xmp_core/UnicodeConversions.cpp
@@ -0,0 +1,1654 @@
+// =================================================================================================
+// Copyright 2004 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.
+// =================================================================================================
+
+#include "public/include/XMP_Const.h"
+
+#define UC_Assert(cond) /* Nothing for now, should be XMP_Assert. */
+#define UC_Throw(msg,id) throw XMP_Error ( id, msg )
+
+#include "UnicodeConversions.hpp"
+
+#if SUNOS_SPARC || XMP_IOS_ARM
+ #include "string.h"
+#endif
+
+using namespace std;
+
+// =================================================================================================
+
+// *** Look into using asm inlines, e.g. count-leading bits for multi-byte UTF-8.
+
+CodePoint_to_UTF16_Proc CodePoint_to_UTF16BE = 0;
+CodePoint_to_UTF16_Proc CodePoint_to_UTF16LE = 0;
+
+CodePoint_from_UTF16_Proc CodePoint_from_UTF16BE = 0;
+CodePoint_from_UTF16_Proc CodePoint_from_UTF16LE = 0;
+
+UTF8_to_UTF16_Proc UTF8_to_UTF16BE = 0;
+UTF8_to_UTF16_Proc UTF8_to_UTF16LE = 0;
+UTF8_to_UTF32_Proc UTF8_to_UTF32BE = 0;
+UTF8_to_UTF32_Proc UTF8_to_UTF32LE = 0;
+
+UTF16_to_UTF8_Proc UTF16BE_to_UTF8 = 0;
+UTF16_to_UTF8_Proc UTF16LE_to_UTF8 = 0;
+UTF32_to_UTF8_Proc UTF32BE_to_UTF8 = 0;
+UTF32_to_UTF8_Proc UTF32LE_to_UTF8 = 0;
+
+UTF8_to_UTF16_Proc UTF8_to_UTF16Native = 0;
+UTF8_to_UTF32_Proc UTF8_to_UTF32Native = 0;
+UTF16_to_UTF8_Proc UTF16Native_to_UTF8 = 0;
+UTF32_to_UTF8_Proc UTF32Native_to_UTF8 = 0;
+
+UTF16_to_UTF32_Proc UTF16BE_to_UTF32BE = 0;
+UTF16_to_UTF32_Proc UTF16BE_to_UTF32LE = 0;
+UTF16_to_UTF32_Proc UTF16LE_to_UTF32BE = 0;
+UTF16_to_UTF32_Proc UTF16LE_to_UTF32LE = 0;
+
+UTF32_to_UTF16_Proc UTF32BE_to_UTF16BE = 0;
+UTF32_to_UTF16_Proc UTF32BE_to_UTF16LE = 0;
+UTF32_to_UTF16_Proc UTF32LE_to_UTF16BE = 0;
+UTF32_to_UTF16_Proc UTF32LE_to_UTF16LE = 0;
+
+// -------------------------------------------------------------------------------------------------
+
+static size_t swap32to16Offset = 0; // Offset to "convert" a swapped UTF32 pointer into a swapped UTF16 pointer.
+
+// -------------------------------------------------------------------------------------------------
+
+static void CodePoint_to_UTF16Nat ( const UTF32Unit cpIn, UTF16Unit * utf16Out, const size_t utf16Len, size_t * utf16Written );
+static void CodePoint_to_UTF16Swp ( const UTF32Unit cpIn, UTF16Unit * utf16Out, const size_t utf16Len, size_t * utf16Written );
+
+static void CodePoint_from_UTF16Nat ( const UTF16Unit * utf16In, const size_t utf16Len, UTF32Unit * cpOut, size_t * utf16Read );
+static void CodePoint_from_UTF16Swp ( const UTF16Unit * utf16In, const size_t utf16Len, UTF32Unit * cpOut, size_t * utf16Read );
+
+// -------------------------------------------------------------------------------------------------
+
+static void UTF8_to_UTF16Nat ( const UTF8Unit * utf8In, const size_t utf8Len,
+ UTF16Unit * utf16Out, const size_t utf16Len,
+ size_t * utf8Read, size_t * utf16Written );
+
+static void UTF8_to_UTF16Swp ( const UTF8Unit * utf8In, const size_t utf8Len,
+ UTF16Unit * utf16Out, const size_t utf16Len,
+ size_t * utf8Read, size_t * utf16Written );
+
+static void UTF8_to_UTF32Nat ( const UTF8Unit * utf8In, const size_t utf8Len,
+ UTF32Unit * utf32Out, const size_t utf32Len,
+ size_t * utf8Read, size_t * utf32Written );
+
+static void UTF8_to_UTF32Swp ( const UTF8Unit * utf8In, const size_t utf8Len,
+ UTF32Unit * utf32Out, const size_t utf32Len,
+ size_t * utf8Read, size_t * utf32Written );
+
+// -------------------------------------------------------------------------------------------------
+
+static void UTF16Nat_to_UTF8 ( const UTF16Unit * utf16In, const size_t utf16Len,
+ UTF8Unit * utf8Out, const size_t utf8Len,
+ size_t * utf16Read, size_t * utf8Written );
+
+static void UTF16Swp_to_UTF8 ( const UTF16Unit * utf16In, const size_t utf16Len,
+ UTF8Unit * utf8Out, const size_t utf8Len,
+ size_t * utf16Read, size_t * utf8Written );
+
+static void UTF32Nat_to_UTF8 ( const UTF32Unit * utf32In, const size_t utf32Len,
+ UTF8Unit * utf8Out, const size_t utf8Len,
+ size_t * utf32Read, size_t * utf8Written );
+
+static void UTF32Swp_to_UTF8 ( const UTF32Unit * utf32In, const size_t utf32Len,
+ UTF8Unit * utf8Out, const size_t utf8Len,
+ size_t * utf32Read, size_t * utf8Written );
+
+// -------------------------------------------------------------------------------------------------
+
+static void UTF16Nat_to_UTF32Nat ( const UTF16Unit * utf16In, const size_t utf16Len,
+ UTF32Unit * utf32Out, const size_t utf32Len,
+ size_t * utf16Read, size_t * utf32Written );
+
+static void UTF16Nat_to_UTF32Swp ( const UTF16Unit * utf16In, const size_t utf16Len,
+ UTF32Unit * utf32Out, const size_t utf32Len,
+ size_t * utf16Read, size_t * utf32Written );
+
+static void UTF16Swp_to_UTF32Nat ( const UTF16Unit * utf16In, const size_t utf16Len,
+ UTF32Unit * utf32Out, const size_t utf32Len,
+ size_t * utf16Read, size_t * utf32Written );
+
+static void UTF16Swp_to_UTF32Swp ( const UTF16Unit * utf16In, const size_t utf16Len,
+ UTF32Unit * utf32Out, const size_t utf32Len,
+ size_t * utf16Read, size_t * utf32Written );
+
+// -------------------------------------------------------------------------------------------------
+
+static void UTF32Nat_to_UTF16Nat ( const UTF32Unit * utf32In, const size_t utf32Len,
+ UTF16Unit * utf16Out, const size_t utf16Len,
+ size_t * utf32Read, size_t * utf16Written );
+
+static void UTF32Nat_to_UTF16Swp ( const UTF32Unit * utf32In, const size_t utf32Len,
+ UTF16Unit * utf16Out, const size_t utf16Len,
+ size_t * utf32Read, size_t * utf16Written );
+
+static void UTF32Swp_to_UTF16Nat ( const UTF32Unit * utf32In, const size_t utf32Len,
+ UTF16Unit * utf16Out, const size_t utf16Len,
+ size_t * utf32Read, size_t * utf16Written );
+
+static void UTF32Swp_to_UTF16Swp ( const UTF32Unit * utf32In, const size_t utf32Len,
+ UTF16Unit * utf16Out, const size_t utf16Len,
+ size_t * utf32Read, size_t * utf16Written );
+
+// =================================================================================================
+
+void InitializeUnicodeConversions()
+{
+ UC_Assert ( (sizeof(UTF8Unit) == 1) && (sizeof(UTF16Unit) == 2) && (sizeof(UTF32Unit) == 4) );
+
+ UTF16Unit u16 = 0x00FF;
+ bool bigEndian = (*((UTF8Unit*)&u16) == 0);
+
+ UTF8_to_UTF16Native = UTF8_to_UTF16Nat;
+ UTF8_to_UTF32Native = UTF8_to_UTF32Nat;
+ UTF16Native_to_UTF8 = UTF16Nat_to_UTF8;
+ UTF32Native_to_UTF8 = UTF32Nat_to_UTF8;
+
+ if ( bigEndian ) {
+
+ swap32to16Offset = 0;
+
+ CodePoint_to_UTF16BE = CodePoint_to_UTF16Nat;
+ CodePoint_to_UTF16LE = CodePoint_to_UTF16Swp;
+
+ CodePoint_from_UTF16BE = CodePoint_from_UTF16Nat;
+ CodePoint_from_UTF16LE = CodePoint_from_UTF16Swp;
+
+ UTF8_to_UTF16BE = UTF8_to_UTF16Nat;
+ UTF8_to_UTF16LE = UTF8_to_UTF16Swp;
+ UTF8_to_UTF32BE = UTF8_to_UTF32Nat;
+ UTF8_to_UTF32LE = UTF8_to_UTF32Swp;
+
+ UTF16BE_to_UTF8 = UTF16Nat_to_UTF8;
+ UTF16LE_to_UTF8 = UTF16Swp_to_UTF8;
+ UTF32BE_to_UTF8 = UTF32Nat_to_UTF8;
+ UTF32LE_to_UTF8 = UTF32Swp_to_UTF8;
+
+ UTF16BE_to_UTF32BE = UTF16Nat_to_UTF32Nat;
+ UTF16BE_to_UTF32LE = UTF16Nat_to_UTF32Swp;
+ UTF16LE_to_UTF32BE = UTF16Swp_to_UTF32Nat;
+ UTF16LE_to_UTF32LE = UTF16Swp_to_UTF32Swp;
+
+ UTF32BE_to_UTF16BE = UTF32Nat_to_UTF16Nat;
+ UTF32BE_to_UTF16LE = UTF32Nat_to_UTF16Swp;
+ UTF32LE_to_UTF16BE = UTF32Swp_to_UTF16Nat;
+ UTF32LE_to_UTF16LE = UTF32Swp_to_UTF16Swp;
+
+ } else {
+
+ swap32to16Offset = 1; // ! Offset in UTF16 units!
+
+ CodePoint_to_UTF16BE = CodePoint_to_UTF16Swp;
+ CodePoint_to_UTF16LE = CodePoint_to_UTF16Nat;
+
+ CodePoint_from_UTF16BE = CodePoint_from_UTF16Swp;
+ CodePoint_from_UTF16LE = CodePoint_from_UTF16Nat;
+
+ UTF8_to_UTF16BE = UTF8_to_UTF16Swp;
+ UTF8_to_UTF16LE = UTF8_to_UTF16Nat;
+ UTF8_to_UTF32BE = UTF8_to_UTF32Swp;
+ UTF8_to_UTF32LE = UTF8_to_UTF32Nat;
+
+ UTF16BE_to_UTF8 = UTF16Swp_to_UTF8;
+ UTF16LE_to_UTF8 = UTF16Nat_to_UTF8;
+ UTF32BE_to_UTF8 = UTF32Swp_to_UTF8;
+ UTF32LE_to_UTF8 = UTF32Nat_to_UTF8;
+
+ UTF16BE_to_UTF32BE = UTF16Swp_to_UTF32Swp;
+ UTF16BE_to_UTF32LE = UTF16Swp_to_UTF32Nat;
+ UTF16LE_to_UTF32BE = UTF16Nat_to_UTF32Swp;
+ UTF16LE_to_UTF32LE = UTF16Nat_to_UTF32Nat;
+
+ UTF32BE_to_UTF16BE = UTF32Swp_to_UTF16Swp;
+ UTF32BE_to_UTF16LE = UTF32Swp_to_UTF16Nat;
+ UTF32LE_to_UTF16BE = UTF32Nat_to_UTF16Swp;
+ UTF32LE_to_UTF16LE = UTF32Nat_to_UTF16Nat;
+
+ }
+
+} // InitializeUnicodeConversions
+
+// =================================================================================================
+
+#if SUNOS_SPARC || XMP_IOS_ARM
+ #define DefineAndGetValue(type,inPtr) type inUnit; memcpy ( &inUnit, inPtr, sizeof(type) );
+#else
+ #define DefineAndGetValue(type,inPtr) type inUnit = *((type *)inPtr);
+#endif
+
+static inline UTF16Unit UTF16InSwap ( const void * inPtr )
+{
+ DefineAndGetValue ( UTF16Unit, inPtr );
+ return (inUnit << 8) | (inUnit >> 8);
+}
+static inline UTF32Unit UTF32InSwap ( const void * inPtr )
+{
+ DefineAndGetValue ( UTF32Unit, inPtr );
+ return (inUnit << 24) | ((inUnit << 8) & 0x00FF0000) | ((inUnit >> 8) & 0x0000FF00) | (inUnit >> 24);
+}
+
+static inline void UTF16OutSwap ( UTF16Unit * outPtr, const UTF16Unit value )
+{
+ UTF16Unit outUnit = (value << 8) | (value >> 8);
+ *outPtr = outUnit;
+}
+
+static inline void UTF32OutSwap ( UTF32Unit * outPtr, const UTF32Unit value )
+{
+ UTF32Unit outUnit = (value << 24) | ((value << 8) & 0x00FF0000) | ((value >> 8) & 0x0000FF00) | (value >> 24);
+ *outPtr = outUnit;
+}
+
+// =================================================================================================
+
+void SwapUTF16 ( const UTF16Unit * utf16In, UTF16Unit * utf16Out, const size_t utf16Len )
+{
+ for ( size_t i = 0; i < utf16Len; ++i ) utf16Out[i] = UTF16InSwap(utf16In+i);
+}
+
+void SwapUTF32 ( const UTF32Unit * utf32In, UTF32Unit * utf32Out, const size_t utf32Len ) {
+ for ( size_t i = 0; i < utf32Len; ++i ) utf32Out[i] = UTF32InSwap(utf32In+i);
+}
+
+// =================================================================================================
+
+extern void ToUTF16 ( const UTF8Unit * utf8In, size_t utf8Len, std::string * utf16Str, bool bigEndian )
+{
+ UTF8_to_UTF16_Proc Converter = UTF8_to_UTF16LE;
+ if ( bigEndian ) Converter = UTF8_to_UTF16BE;
+
+ enum { kBufferSize = 8*1024 };
+ UTF16Unit u16Buffer[kBufferSize]; // 16K bytes
+ size_t readCount, writeCount;
+
+ utf16Str->erase();
+ utf16Str->reserve ( 2*utf8Len ); // As good a guess as any.
+
+ while ( utf8Len > 0 ) {
+ Converter ( utf8In, utf8Len, u16Buffer, kBufferSize, &readCount, &writeCount );
+ if ( writeCount == 0 ) UC_Throw ( "Incomplete Unicode at end of string", kXMPErr_BadXML );
+ utf16Str->append ( (const char *)u16Buffer, writeCount*2 );
+ utf8In += readCount;
+ utf8Len -= readCount;
+ }
+
+} // ToUTF16
+
+// =================================================================================================
+
+extern void ToUTF16Native ( const UTF8Unit * utf8In, size_t utf8Len, std::string * utf16Str )
+{
+ enum { kBufferSize = 8*1024 };
+ UTF16Unit u16Buffer[kBufferSize]; // 16K bytes
+ size_t readCount, writeCount;
+
+ utf16Str->erase();
+ utf16Str->reserve ( 2*utf8Len ); // As good a guess as any.
+
+ while ( utf8Len > 0 ) {
+ UTF8_to_UTF16Nat ( utf8In, utf8Len, u16Buffer, kBufferSize, &readCount, &writeCount );
+ if ( writeCount == 0 ) UC_Throw ( "Incomplete Unicode at end of string", kXMPErr_BadXML );
+ utf16Str->append ( (const char *)u16Buffer, writeCount*2 );
+ utf8In += readCount;
+ utf8Len -= readCount;
+ }
+
+} // ToUTF16Native
+
+// =================================================================================================
+
+extern void ToUTF32 ( const UTF8Unit * utf8In, size_t utf8Len, std::string * utf32Str, bool bigEndian )
+{
+ UTF8_to_UTF32_Proc Converter = UTF8_to_UTF32LE;
+ if ( bigEndian ) Converter = UTF8_to_UTF32BE;
+
+ enum { kBufferSize = 4*1024 };
+ UTF32Unit u32Buffer[kBufferSize]; // 16K bytes
+ size_t readCount, writeCount;
+
+ utf32Str->erase();
+ utf32Str->reserve ( 4*utf8Len ); // As good a guess as any.
+
+ while ( utf8Len > 0 ) {
+ Converter ( utf8In, utf8Len, u32Buffer, kBufferSize, &readCount, &writeCount );
+ if ( writeCount == 0 ) UC_Throw ( "Incomplete Unicode at end of string", kXMPErr_BadXML );
+ utf32Str->append ( (const char *)u32Buffer, writeCount*4 );
+ utf8In += readCount;
+ utf8Len -= readCount;
+ }
+
+} // ToUTF32
+
+// =================================================================================================
+
+extern void ToUTF32Native ( const UTF8Unit * utf8In, size_t utf8Len, std::string * utf32Str )
+{
+ enum { kBufferSize = 4*1024 };
+ UTF32Unit u32Buffer[kBufferSize]; // 16K bytes
+ size_t readCount, writeCount;
+
+ utf32Str->erase();
+ utf32Str->reserve ( 4*utf8Len ); // As good a guess as any.
+
+ while ( utf8Len > 0 ) {
+ UTF8_to_UTF32Nat ( utf8In, utf8Len, u32Buffer, kBufferSize, &readCount, &writeCount );
+ if ( writeCount == 0 ) UC_Throw ( "Incomplete Unicode at end of string", kXMPErr_BadXML );
+ utf32Str->append ( (const char *)u32Buffer, writeCount*4 );
+ utf8In += readCount;
+ utf8Len -= readCount;
+ }
+
+} // ToUTF32Native
+
+// =================================================================================================
+
+extern void FromUTF16 ( const UTF16Unit * utf16In, size_t utf16Len, std::string * utf8Str, bool bigEndian )
+{
+ UTF16_to_UTF8_Proc Converter = UTF16LE_to_UTF8;
+ if ( bigEndian ) Converter = UTF16BE_to_UTF8;
+
+ enum { kBufferSize = 16*1024 };
+ UTF8Unit u8Buffer[kBufferSize];
+ size_t readCount, writeCount;
+
+ utf8Str->erase();
+ utf8Str->reserve ( 2*utf16Len ); // As good a guess as any.
+
+ while ( utf16Len > 0 ) {
+ Converter ( utf16In, utf16Len, u8Buffer, kBufferSize, &readCount, &writeCount );
+ if ( writeCount == 0 ) UC_Throw ( "Incomplete Unicode at end of string", kXMPErr_BadXML );
+ utf8Str->append ( (const char *)u8Buffer, writeCount );
+ utf16In += readCount;
+ utf16Len -= readCount;
+ }
+
+} // FromUTF16
+
+// =================================================================================================
+
+extern void FromUTF16Native ( const UTF16Unit * utf16In, size_t utf16Len, std::string * utf8Str )
+{
+ enum { kBufferSize = 16*1024 };
+ UTF8Unit u8Buffer[kBufferSize];
+ size_t readCount, writeCount;
+
+ utf8Str->erase();
+ utf8Str->reserve ( 2*utf16Len ); // As good a guess as any.
+
+ while ( utf16Len > 0 ) {
+ UTF16Nat_to_UTF8 ( utf16In, utf16Len, u8Buffer, kBufferSize, &readCount, &writeCount );
+ if ( writeCount == 0 ) UC_Throw ( "Incomplete Unicode at end of string", kXMPErr_BadXML );
+ utf8Str->append ( (const char *)u8Buffer, writeCount );
+ utf16In += readCount;
+ utf16Len -= readCount;
+ }
+
+} // FromUTF16Native
+
+// =================================================================================================
+
+extern void FromUTF32 ( const UTF32Unit * utf32In, size_t utf32Len, std::string * utf8Str, bool bigEndian )
+{
+ UTF32_to_UTF8_Proc Converter = UTF32LE_to_UTF8;
+ if ( bigEndian ) Converter = UTF32BE_to_UTF8;
+
+ enum { kBufferSize = 16*1024 };
+ UTF8Unit u8Buffer[kBufferSize];
+ size_t readCount, writeCount;
+
+ utf8Str->erase();
+ utf8Str->reserve ( 2*utf32Len ); // As good a guess as any.
+
+ while ( utf32Len > 0 ) {
+ Converter ( utf32In, utf32Len, u8Buffer, kBufferSize, &readCount, &writeCount );
+ if ( writeCount == 0 ) UC_Throw ( "Incomplete Unicode at end of string", kXMPErr_BadXML );
+ utf8Str->append ( (const char *)u8Buffer, writeCount );
+ utf32In += readCount;
+ utf32Len -= readCount;
+ }
+
+} // FromUTF32
+
+// =================================================================================================
+
+extern void FromUTF32Native ( const UTF32Unit * utf32In, size_t utf32Len, std::string * utf8Str )
+{
+ enum { kBufferSize = 16*1024 };
+ UTF8Unit u8Buffer[kBufferSize];
+ size_t readCount, writeCount;
+
+ utf8Str->erase();
+ utf8Str->reserve ( 2*utf32Len ); // As good a guess as any.
+
+ while ( utf32Len > 0 ) {
+ UTF32Nat_to_UTF8 ( utf32In, utf32Len, u8Buffer, kBufferSize, &readCount, &writeCount );
+ if ( writeCount == 0 ) UC_Throw ( "Incomplete Unicode at end of string", kXMPErr_BadXML );
+ utf8Str->append ( (const char *)u8Buffer, writeCount );
+ utf32In += readCount;
+ utf32Len -= readCount;
+ }
+
+} // FromUTF32Native
+
+// =================================================================================================
+
+static void CodePoint_to_UTF8_Multi ( const UTF32Unit cpIn, UTF8Unit * utf8Out, const size_t utf8Len, size_t * utf8Written )
+{
+ size_t unitCount = 0;
+
+ if ( cpIn > 0x10FFFF ) UC_Throw ( "Bad UTF-32 - out of range", kXMPErr_BadParam );
+ if ( (0xD800 <= cpIn) && (cpIn <= 0xDFFF) ) UC_Throw ( "Bad UTF-32 - surrogate code point", kXMPErr_BadParam );
+
+ // Compute the number of bytes using 6 data bits each. Then see if the highest order bits will
+ // fit into the leading byte. Write the UTF-8 sequence if there is enough room.
+
+ UTF32Unit temp, mask;
+ size_t bytesNeeded = 0;
+ for ( temp = cpIn; temp != 0; temp = temp >> 6 ) ++bytesNeeded;
+
+ temp = cpIn >> ((bytesNeeded-1)*6); // The highest order data bits.
+ mask = (0x80 >> bytesNeeded) - 1; // Available data bits in the leading byte.
+ if ( temp > mask ) ++bytesNeeded;
+
+ if ( bytesNeeded > utf8Len ) goto Done; // Not enough room for the output.
+ unitCount = bytesNeeded;
+
+ temp = cpIn;
+ for ( --bytesNeeded; bytesNeeded > 0; --bytesNeeded ) {
+ utf8Out[bytesNeeded] = 0x80 | UTF8Unit ( temp & 0x3F );
+ temp = temp >> 6;
+ }
+
+ mask = ~((1 << (8-unitCount)) - 1);
+ utf8Out[0] = UTF8Unit ( mask | temp );
+
+Done:
+ *utf8Written = unitCount;
+ return;
+
+} // CodePoint_to_UTF8_Multi
+
+// =================================================================================================
+
+void CodePoint_to_UTF8 ( const UTF32Unit cpIn, UTF8Unit * utf8Out, const size_t utf8Len, size_t * utf8Written )
+{
+ size_t unitCount = 0;
+
+ UC_Assert ( (utf8Out != 0) && (utf8Written != 0) );
+ if ( utf8Len == 0 ) goto Done;
+ if ( cpIn > 0x7F ) goto MultiByte; // ! Force linear execution path for ASCII.
+
+ unitCount = 1;
+ *utf8Out = UTF8Unit(cpIn);
+
+Done:
+ *utf8Written = unitCount;
+ return;
+
+MultiByte:
+ CodePoint_to_UTF8_Multi( cpIn, utf8Out, utf8Len, utf8Written );
+ return;
+
+} // CodePoint_to_UTF8
+
+// =================================================================================================
+
+static void CodePoint_from_UTF8_Multi ( const UTF8Unit * utf8In, const size_t utf8Len, UTF32Unit * cpOut, size_t * utf8Read )
+{
+ UTF8Unit inUnit = *utf8In;
+ size_t unitCount = 0;
+ UTF32Unit cp; // ! Avoid gcc complaints about declarations after goto's.
+ const UTF8Unit * utf8Pos;
+
+ // -------------------------------------------------------------------------------------
+ // We've got a multibyte UTF-8 character. The first byte has the number of bytes and the
+ // highest order data bits. The other bytes each add 6 more data bits.
+
+ #if 0 // This might be a more effcient way to count the bytes.
+ static XMP_Uns8 kByteCounts[16] = { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 2, 2, 3, 4 };
+ size_t bytesNeeded = kByteCounts [ inUnit >> 4 ];
+ if ( (bytesNeeded < 2) || ((bytesNeeded == 4) && ((inUnit & 0x08) != 0)) ) {
+ UC_Throw ( "Invalid UTF-8 sequence length", kXMPErr_BadParam );
+ }
+ #endif
+
+ size_t bytesNeeded = 0; // Count the leading 1 bits in the first byte.
+ for ( UTF8Unit temp = inUnit; temp > 0x7F; temp = temp << 1 ) ++bytesNeeded;
+ // *** Consider CPU-specific assembly inline, e.g. cntlzw on PowerPC.
+
+ if ( (bytesNeeded < 2) || (bytesNeeded > 4) ) UC_Throw ( "Invalid UTF-8 sequence length", kXMPErr_BadParam );
+ if ( bytesNeeded > utf8Len ) goto Done; // Not enough input in this buffer.
+ unitCount = bytesNeeded;
+
+ cp = inUnit & ((1 << (7-unitCount)) - 1); // Isolate the initial data bits in the bottom of cp.
+
+ utf8Pos = utf8In + 1; // We've absorbed the first byte.
+ for ( --bytesNeeded; bytesNeeded > 0; --bytesNeeded, ++utf8Pos ) {
+ inUnit = *utf8Pos;
+ if ( (inUnit & UTF8Unit(0xC0)) != UTF8Unit(0x80) ) UC_Throw ( "Invalid UTF-8 data byte", kXMPErr_BadParam );
+ cp = (cp << 6) | (inUnit & 0x3F);
+ }
+
+ if ( cp >= 0xD800 ) { // Skip the next comparisons most of the time.
+ if ( (0xD800 <= cp) && (cp <= 0xDFFF) ) UC_Throw ( "Bad UTF-8 - surrogate code point", kXMPErr_BadParam );
+ if ( cp > 0x10FFFF ) UC_Throw ( "Bad UTF-8 - out of range", kXMPErr_BadParam );
+ }
+
+ *cpOut = cp; // ! Don't put after Done, don't write if no input.
+
+Done:
+ *utf8Read = unitCount;
+ return;
+
+} // CodePoint_from_UTF8_Multi
+
+// =================================================================================================
+
+void CodePoint_from_UTF8 ( const UTF8Unit * utf8In, const size_t utf8Len, UTF32Unit * cpOut, size_t * utf8Read )
+{
+ UTF8Unit inUnit; // ! Don't read until we know there is input.
+ size_t unitCount = 0;
+
+ UC_Assert ( (utf8In != 0) && (cpOut != 0) && (utf8Read != 0) );
+ if ( utf8Len == 0 ) goto Done;
+ inUnit = *utf8In;
+ if ( inUnit >= 0x80 ) goto MultiByte; // ! Force linear execution path for ASCII.
+
+ unitCount = 1;
+ *cpOut = inUnit; // ! Don't put after Done, don't write if no input.
+
+Done:
+ *utf8Read = unitCount;
+ return;
+
+MultiByte:
+ CodePoint_from_UTF8_Multi ( utf8In, utf8Len, cpOut, utf8Read );
+ return;
+
+} // CodePoint_from_UTF8
+
+// =================================================================================================
+
+static void CodePoint_to_UTF16Nat_Surrogate ( const UTF32Unit cpIn, UTF16Unit * utf16Out, const size_t utf16Len, size_t * utf16Written )
+{
+ size_t unitCount = 0;
+ UTF32Unit temp; // ! Avoid gcc complaints about declarations after goto's.
+
+ if ( cpIn > 0x10FFFF ) UC_Throw ( "Bad UTF-32 - out of range", kXMPErr_BadParam );
+ if ( utf16Len < 2 ) goto Done; // Not enough room for the output.
+
+ unitCount = 2;
+ temp = cpIn - 0x10000;
+ utf16Out[0] = 0xD800 | UTF16Unit ( temp >> 10 );
+ utf16Out[1] = 0xDC00 | UTF16Unit ( temp & 0x3FF );
+
+Done:
+ *utf16Written = unitCount;
+ return;
+
+} // CodePoint_to_UTF16Nat_Surrogate
+
+// =================================================================================================
+
+static void CodePoint_to_UTF16Nat ( const UTF32Unit cpIn, UTF16Unit * utf16Out, const size_t utf16Len, size_t * utf16Written )
+{
+ size_t unitCount = 0;
+
+ UC_Assert ( (utf16Out != 0) && (utf16Written != 0) );
+ if ( utf16Len == 0 ) goto Done;
+ if ( cpIn >= 0xD800 ) goto CheckSurrogate; // ! Force linear execution path for the BMP.
+
+InBMP:
+ unitCount = 1;
+ *utf16Out = UTF16Unit(cpIn);
+
+Done:
+ *utf16Written = unitCount;
+ return;
+
+CheckSurrogate:
+ if ( cpIn > 0xFFFF ) goto SurrogatePair;
+ if ( cpIn > 0xDFFF ) goto InBMP;
+ UC_Throw ( "Bad UTF-32 - surrogate code point", kXMPErr_BadParam );
+
+SurrogatePair:
+ CodePoint_to_UTF16Nat_Surrogate ( cpIn, utf16Out, utf16Len, utf16Written );
+ return;
+
+} // CodePoint_to_UTF16Nat
+
+// =================================================================================================
+
+static void CodePoint_from_UTF16Nat_Surrogate ( const UTF16Unit * utf16In, const size_t utf16Len, UTF32Unit * cpOut, size_t * utf16Read )
+{
+ UTF16Unit hiUnit = *utf16In;
+ size_t unitCount = 0;
+ UTF16Unit loUnit; // ! Avoid gcc complaints about declarations after goto's.
+ UTF32Unit cp;
+
+ // ----------------------------------
+ // We've got a UTF-16 surrogate pair.
+
+ if ( hiUnit > 0xDBFF ) UC_Throw ( "Bad UTF-16 - leading low surrogate", kXMPErr_BadParam );
+ if ( utf16Len < 2 ) goto Done; // Not enough input in this buffer.
+
+ loUnit = *(utf16In+1);
+ if ( (loUnit < 0xDC00) || (0xDFFF < loUnit) ) UC_Throw ( "Bad UTF-16 - missing low surrogate", kXMPErr_BadParam );
+
+ unitCount = 2;
+ cp = (((hiUnit & 0x3FF) << 10) | (loUnit & 0x3FF)) + 0x10000;
+
+ *cpOut = cp; // ! Don't put after Done, don't write if no input.
+
+Done:
+ *utf16Read = unitCount;
+ return;
+
+} // CodePoint_from_UTF16Nat_Surrogate
+
+// =================================================================================================
+
+static void CodePoint_from_UTF16Nat ( const UTF16Unit * utf16In, const size_t utf16Len, UTF32Unit * cpOut, size_t * utf16Read )
+{
+ UTF16Unit inUnit; // ! Don't read until we know there is input.
+ size_t unitCount = 0;
+
+ UC_Assert ( (utf16In != 0) && (cpOut != 0) && (utf16Read != 0) );
+ if ( utf16Len == 0 ) goto Done;
+ inUnit = *utf16In;
+ if ( (0xD800 <= inUnit) && (inUnit <= 0xDFFF) ) goto SurrogatePair; // ! Force linear execution path for the BMP.
+
+ unitCount = 1;
+ *cpOut = inUnit; // ! Don't put after Done, don't write if no input.
+
+Done:
+ *utf16Read = unitCount;
+ return;
+
+SurrogatePair:
+ CodePoint_from_UTF16Nat_Surrogate ( utf16In, utf16Len, cpOut, utf16Read );
+ return;
+
+} // CodePoint_from_UTF16Nat
+
+// =================================================================================================
+
+static void UTF8_to_UTF16Nat ( const UTF8Unit * utf8In, const size_t utf8Len,
+ UTF16Unit * utf16Out, const size_t utf16Len,
+ size_t * utf8Read, size_t * utf16Written )
+{
+ const UTF8Unit * utf8Pos = utf8In;
+ UTF16Unit * utf16Pos = utf16Out;
+
+ size_t utf8Left = utf8Len;
+ size_t utf16Left = utf16Len;
+
+ UC_Assert ( (utf8In != 0) && (utf16Out != 0) && (utf8Read != 0) && (utf16Written != 0) );
+
+ while ( (utf8Left > 0) && (utf16Left > 0) ) {
+
+ // Do a run of ASCII, it copies 1 input unit into 1 output unit.
+ size_t i, limit = utf8Left;
+ if ( limit > utf16Left ) limit = utf16Left;
+ for ( i = 0; i < limit; ++i ) {
+ UTF8Unit inUnit = *utf8Pos;
+ if ( inUnit > 0x7F ) break;
+ *utf16Pos = inUnit;
+ ++utf8Pos;
+ ++utf16Pos;
+ }
+ utf8Left -= i;
+ utf16Left -= i;
+
+ // Do a run of non-ASCII, it copies multiple input units into 1 or 2 output units.
+ while ( (utf8Left > 0) && (utf16Left > 0) ) {
+ UTF32Unit cp;
+ size_t len8, len16;
+ UTF8Unit inUnit = *utf8Pos;
+ if ( inUnit <= 0x7F ) break;
+ CodePoint_from_UTF8_Multi ( utf8Pos, utf8Left, &cp, &len8 );
+ if ( len8 == 0 ) goto Done; // The input buffer ends in the middle of a character.
+ if ( cp <= 0xFFFF ) {
+ *utf16Pos = UTF16Unit(cp);
+ len16 = 1;
+ } else {
+ CodePoint_to_UTF16Nat_Surrogate ( cp, utf16Pos, utf16Left, &len16 );
+ if ( len16 == 0 ) goto Done; // Not enough room in the output buffer.
+ }
+ utf8Left -= len8;
+ utf8Pos += len8;
+ utf16Left -= len16;
+ utf16Pos += len16;
+ }
+
+ }
+
+Done: // Set the output lengths.
+ *utf8Read = utf8Len - utf8Left;
+ *utf16Written = utf16Len - utf16Left;
+
+} // UTF8_to_UTF16Nat
+
+// =================================================================================================
+
+static void UTF8_to_UTF32Nat ( const UTF8Unit * utf8In, const size_t utf8Len,
+ UTF32Unit * utf32Out, const size_t utf32Len,
+ size_t * utf8Read, size_t * utf32Written )
+{
+ const UTF8Unit * utf8Pos = utf8In;
+ UTF32Unit * utf32Pos = utf32Out;
+
+ size_t utf8Left = utf8Len;
+ size_t utf32Left = utf32Len;
+
+ UC_Assert ( (utf8In != 0) && (utf32Out != 0) && (utf8Read != 0) && (utf32Written != 0) );
+
+ while ( (utf8Left > 0) && (utf32Left > 0) ) {
+
+ // Do a run of ASCII, it copies 1 input unit into 1 output unit.
+ size_t i, limit = utf8Left;
+ if ( limit > utf32Left ) limit = utf32Left;
+ for ( i = 0; i < limit; ++i ) {
+ UTF8Unit inUnit = *utf8Pos;
+ if ( inUnit > 0x7F ) break;
+ *utf32Pos = inUnit;
+ ++utf8Pos;
+ ++utf32Pos;
+ }
+ utf8Left -= i;
+ utf32Left -= i;
+
+ // Do a run of non-ASCII, it copies variable input into 1 output unit.
+ while ( (utf8Left > 0) && (utf32Left > 0) ) {
+ size_t len;
+ UTF8Unit inUnit = *utf8Pos;
+ if ( inUnit <= 0x7F ) break;
+ CodePoint_from_UTF8_Multi ( utf8Pos, utf8Left, utf32Pos, &len );
+ if ( len == 0 ) goto Done; // The input buffer ends in the middle of a character.
+ utf8Left -= len;
+ utf8Pos += len;
+ utf32Left -= 1;
+ utf32Pos += 1;
+ }
+
+ }
+
+Done: // Set the output lengths.
+ *utf8Read = utf8Len - utf8Left;
+ *utf32Written = utf32Len - utf32Left;
+
+} // UTF8_to_UTF32Nat
+
+// =================================================================================================
+
+static void UTF16Nat_to_UTF8 ( const UTF16Unit * utf16In, const size_t utf16Len,
+ UTF8Unit * utf8Out, const size_t utf8Len,
+ size_t * utf16Read, size_t * utf8Written )
+{
+ const UTF16Unit * utf16Pos = utf16In;
+ UTF8Unit * utf8Pos = utf8Out;
+
+ size_t utf16Left = utf16Len;
+ size_t utf8Left = utf8Len;
+
+ UC_Assert ( (utf16In != 0) && (utf8Out != 0) && (utf16Read != 0) && (utf8Written != 0) );
+
+ while ( (utf16Left > 0) && (utf8Left > 0) ) {
+
+ // Do a run of ASCII, it copies 1 input unit into 1 output unit.
+ size_t i, limit = utf16Left;
+ if ( limit > utf8Left ) limit = utf8Left;
+ for ( i = 0; i < limit; ++i ) {
+ UTF16Unit inUnit = *utf16Pos;
+ if ( inUnit > 0x7F ) break;
+ *utf8Pos = UTF8Unit(inUnit);
+ ++utf16Pos;
+ ++utf8Pos;
+ }
+ utf16Left -= i;
+ utf8Left -= i;
+
+ // Do a run of non-ASCII inside the BMP, it copies 1 input unit into multiple output units.
+ while ( (utf16Left > 0) && (utf8Left > 0) ) {
+ size_t len8;
+ UTF16Unit inUnit = *utf16Pos;
+ if ( inUnit <= 0x7F ) break;
+ if ( (0xD800 <= inUnit) && (inUnit <= 0xDFFF) ) break;
+ CodePoint_to_UTF8_Multi ( inUnit, utf8Pos, utf8Left, &len8 );
+ if ( len8 == 0 ) goto Done; // Not enough room in the output buffer.
+ utf16Left -= 1;
+ utf16Pos += 1;
+ utf8Left -= len8;
+ utf8Pos += len8;
+ }
+
+ // Do a run of surrogate pairs, it copies 2 input units into multiple output units.
+ while ( (utf16Left > 0) && (utf8Left > 0) ) {
+ UTF32Unit cp;
+ size_t len16, len8;
+ UTF16Unit inUnit = *utf16Pos;
+ if ( (inUnit < 0xD800) || (0xDFFF < inUnit) ) break;
+ CodePoint_from_UTF16Nat_Surrogate ( utf16Pos, utf16Left, &cp, &len16 );
+ if ( len16 == 0 ) goto Done; // The input buffer ends in the middle of a surrogate pair.
+ UC_Assert ( len16 == 2 );
+ CodePoint_to_UTF8_Multi ( cp, utf8Pos, utf8Left, &len8 );
+ if ( len8 == 0 ) goto Done; // Not enough room in the output buffer.
+ utf16Left -= len16;
+ utf16Pos += len16;
+ utf8Left -= len8;
+ utf8Pos += len8;
+ }
+
+ }
+
+Done: // Set the output lengths.
+ *utf16Read = utf16Len - utf16Left;
+ *utf8Written = utf8Len - utf8Left;
+
+} // UTF16Nat_to_UTF8
+
+// =================================================================================================
+
+static void UTF32Nat_to_UTF8 ( const UTF32Unit * utf32In, const size_t utf32Len,
+ UTF8Unit * utf8Out, const size_t utf8Len,
+ size_t * utf32Read, size_t * utf8Written )
+{
+ const UTF32Unit * utf32Pos = utf32In;
+ UTF8Unit * utf8Pos = utf8Out;
+
+ size_t utf32Left = utf32Len;
+ size_t utf8Left = utf8Len;
+
+ UC_Assert ( (utf32In != 0) && (utf8Out != 0) && (utf32Read != 0) && (utf8Written != 0) );
+
+ while ( (utf32Left > 0) && (utf8Left > 0) ) {
+
+ // Do a run of ASCII, it copies 1 input unit into 1 output unit.
+ size_t i, limit = utf32Left;
+ if ( limit > utf8Left ) limit = utf8Left;
+ for ( i = 0; i < limit; ++i ) {
+ UTF32Unit inUnit = *utf32Pos;
+ if ( inUnit > 0x7F ) break;
+ *utf8Pos = UTF8Unit(inUnit);
+ ++utf32Pos;
+ ++utf8Pos;
+ }
+ utf32Left -= i;
+ utf8Left -= i;
+
+ // Do a run of non-ASCII, it copies 1 input unit into multiple output units.
+ while ( (utf32Left > 0) && (utf8Left > 0) ) {
+ size_t len;
+ UTF32Unit inUnit = *utf32Pos;
+ if ( inUnit <= 0x7F ) break;
+ CodePoint_to_UTF8_Multi ( inUnit, utf8Pos, utf8Left, &len );
+ if ( len == 0 ) goto Done; // Not enough room in the output buffer.
+ utf32Left -= 1;
+ utf32Pos += 1;
+ utf8Left -= len;
+ utf8Pos += len;
+ }
+
+ }
+
+Done: // Set the output lengths.
+ *utf32Read = utf32Len - utf32Left;
+ *utf8Written = utf8Len - utf8Left;
+
+} // UTF32Nat_to_UTF8
+
+// =================================================================================================
+
+static void UTF16Nat_to_UTF32Nat ( const UTF16Unit * utf16In, const size_t utf16Len,
+ UTF32Unit * utf32Out, const size_t utf32Len,
+ size_t * utf16Read, size_t * utf32Written )
+{
+ const UTF16Unit * utf16Pos = utf16In;
+ UTF32Unit * utf32Pos = utf32Out;
+
+ size_t utf16Left = utf16Len;
+ size_t utf32Left = utf32Len;
+
+ UC_Assert ( (utf16In != 0) && (utf32Out != 0) && (utf16Read != 0) && (utf32Written != 0) );
+
+ while ( (utf16Left > 0) && (utf32Left > 0) ) {
+
+ // Do a run of BMP, it copies 1 input unit into 1 output unit.
+ size_t i, limit = utf16Left;
+ if ( limit > utf32Left ) limit = utf32Left;
+ for ( i = 0; i < limit; ++i ) {
+ UTF16Unit inUnit = *utf16Pos;
+ if ( (0xD800 <= inUnit) && (inUnit <= 0xDFFF) ) break;
+ *utf32Pos = inUnit;
+ ++utf16Pos;
+ ++utf32Pos;
+ }
+ utf16Left -= i;
+ utf32Left -= i;
+
+ // Do a run of surrogate pairs, it copies 2 input units into 1 output unit.
+ while ( (utf16Left > 0) && (utf32Left > 0) ) {
+ size_t len;
+ UTF16Unit inUnit = *utf16Pos;
+ if ( (inUnit < 0xD800) || (0xDFFF < inUnit) ) break;
+ CodePoint_from_UTF16Nat_Surrogate ( utf16Pos, utf16Left, utf32Pos, &len );
+ if ( len == 0 ) goto Done; // The input buffer ends in the middle of a surrogate pair.
+ UC_Assert ( len == 2 );
+ utf16Left -= len;
+ utf16Pos += len;
+ utf32Left -= 1;
+ utf32Pos += 1;
+ }
+
+ }
+
+Done: // Set the output lengths.
+ *utf16Read = utf16Len - utf16Left;
+ *utf32Written = utf32Len - utf32Left;
+
+} // UTF16Nat_to_UTF32Nat
+
+// =================================================================================================
+
+static void UTF32Nat_to_UTF16Nat ( const UTF32Unit * utf32In, const size_t utf32Len,
+ UTF16Unit * utf16Out, const size_t utf16Len,
+ size_t * utf32Read, size_t * utf16Written )
+{
+ const UTF32Unit * utf32Pos = utf32In;
+ UTF16Unit * utf16Pos = utf16Out;
+
+ size_t utf32Left = utf32Len;
+ size_t utf16Left = utf16Len;
+
+ UC_Assert ( (utf32In != 0) && (utf16Out != 0) && (utf32Read != 0) && (utf16Written != 0) );
+
+ while ( (utf32Left > 0) && (utf16Left > 0) ) {
+
+ // Do a run of BMP, it copies 1 input unit into 1 output unit.
+ size_t i, limit = utf32Left;
+ if ( limit > utf16Left ) limit = utf16Left;
+ for ( i = 0; i < limit; ++i ) {
+ UTF32Unit inUnit = *utf32Pos;
+ if ( inUnit > 0xFFFF ) break;
+ *utf16Pos = UTF16Unit(inUnit);
+ ++utf32Pos;
+ ++utf16Pos;
+ }
+ utf32Left -= i;
+ utf16Left -= i;
+
+ // Do a run of non-BMP, it copies 1 input unit into 2 output units.
+ while ( (utf32Left > 0) && (utf16Left > 0) ) {
+ size_t len;
+ UTF32Unit inUnit = *utf32Pos;
+ if ( inUnit <= 0xFFFF ) break;
+ CodePoint_to_UTF16Nat_Surrogate ( inUnit, utf16Pos, utf16Left, &len );
+ if ( len == 0 ) goto Done; // Not enough room in the output buffer.
+ UC_Assert ( len == 2 );
+ utf32Left -= 1;
+ utf32Pos += 1;
+ utf16Left -= 2;
+ utf16Pos += 2;
+ }
+
+ }
+
+Done: // Set the output lengths.
+ *utf32Read = utf32Len - utf32Left;
+ *utf16Written = utf16Len - utf16Left;
+
+} // UTF32Nat_to_UTF16Nat
+
+// =================================================================================================
+
+static void CodePoint_to_UTF16Swp_Surrogate ( const UTF32Unit cpIn, UTF16Unit * utf16Out, const size_t utf16Len, size_t * utf16Written )
+{
+ size_t unitCount = 0;
+ UTF32Unit temp; // ! Avoid gcc complaints about declarations after goto's.
+
+ if ( cpIn > 0x10FFFF ) UC_Throw ( "Bad UTF-32 - out of range", kXMPErr_BadParam );
+ if ( utf16Len < 2 ) goto Done; // Not enough room for the output.
+
+ unitCount = 2;
+ temp = cpIn - 0x10000;
+ UTF16OutSwap ( &utf16Out[0], (0xD800 | UTF16Unit ( temp >> 10 )) );
+ UTF16OutSwap ( &utf16Out[1], (0xDC00 | UTF16Unit ( temp & 0x3FF)) );
+
+Done:
+ *utf16Written = unitCount;
+ return;
+
+} // CodePoint_to_UTF16Swp_Surrogate
+
+// =================================================================================================
+
+static void CodePoint_to_UTF16Swp ( const UTF32Unit cpIn, UTF16Unit * utf16Out, const size_t utf16Len, size_t * utf16Written )
+{
+ size_t unitCount = 0;
+
+ UC_Assert ( (utf16Out != 0) && (utf16Written != 0) );
+ if ( utf16Len == 0 ) goto Done;
+ if ( cpIn >= 0xD800 ) goto CheckSurrogate; // ! Force linear execution path for the BMP.
+
+InBMP:
+ unitCount = 1;
+ UTF16OutSwap ( utf16Out, UTF16Unit(cpIn) );
+
+Done:
+ *utf16Written = unitCount;
+ return;
+
+CheckSurrogate:
+ if ( cpIn > 0xFFFF ) goto SurrogatePair;
+ if ( cpIn > 0xDFFF ) goto InBMP;
+ UC_Throw ( "Bad UTF-32 - surrogate code point", kXMPErr_BadParam );
+
+SurrogatePair:
+ CodePoint_to_UTF16Swp_Surrogate ( cpIn, utf16Out, utf16Len, utf16Written );
+ return;
+
+} // CodePoint_to_UTF16Swp
+
+// =================================================================================================
+
+static void CodePoint_from_UTF16Swp_Surrogate ( const UTF16Unit * utf16In, const size_t utf16Len, UTF32Unit * cpOut, size_t * utf16Read )
+{
+ UTF16Unit hiUnit = UTF16InSwap(utf16In);
+ size_t unitCount = 0;
+ UTF16Unit loUnit; // ! Avoid gcc complaints about declarations after goto's.
+ UTF32Unit cp;
+
+ // ----------------------------------
+ // We've got a UTF-16 surrogate pair.
+
+ if ( hiUnit > 0xDBFF ) UC_Throw ( "Bad UTF-16 - leading low surrogate", kXMPErr_BadParam );
+ if ( utf16Len < 2 ) goto Done; // Not enough input in this buffer.
+
+ loUnit = UTF16InSwap(utf16In+1);
+ if ( (loUnit < 0xDC00) || (0xDFFF < loUnit) ) UC_Throw ( "Bad UTF-16 - missing low surrogate", kXMPErr_BadParam );
+
+ unitCount = 2;
+ cp = (((hiUnit & 0x3FF) << 10) | (loUnit & 0x3FF)) + 0x10000;
+
+ *cpOut = cp; // ! Don't put after Done, don't write if no input.
+
+Done:
+ *utf16Read = unitCount;
+ return;
+
+} // CodePoint_from_UTF16Swp_Surrogate
+
+// =================================================================================================
+
+static void CodePoint_from_UTF16Swp ( const UTF16Unit * utf16In, const size_t utf16Len, UTF32Unit * cpOut, size_t * utf16Read )
+{
+ UTF16Unit inUnit; // ! Don't read until we know there is input.
+ size_t unitCount = 0;
+
+ UC_Assert ( (utf16In != 0) && (cpOut != 0) && (utf16Read != 0) );
+ if ( utf16Len == 0 ) goto Done;
+ inUnit = UTF16InSwap(utf16In);
+ if ( (0xD800 <= inUnit) && (inUnit <= 0xDFFF) ) goto SurrogatePair; // ! Force linear execution path for the BMP.
+
+ unitCount = 1;
+ *cpOut = inUnit; // ! Don't put after Done, don't write if no input.
+
+Done:
+ *utf16Read = unitCount;
+ return;
+
+SurrogatePair:
+ CodePoint_from_UTF16Swp_Surrogate ( utf16In, utf16Len, cpOut, utf16Read );
+ return;
+
+} // CodePoint_from_UTF16Swp
+
+// =================================================================================================
+
+static void UTF8_to_UTF16Swp ( const UTF8Unit * utf8In, const size_t utf8Len,
+ UTF16Unit * utf16Out, const size_t utf16Len,
+ size_t * utf8Read, size_t * utf16Written )
+{
+ const UTF8Unit * utf8Pos = utf8In;
+ UTF16Unit * utf16Pos = utf16Out;
+
+ size_t utf8Left = utf8Len;
+ size_t utf16Left = utf16Len;
+
+ UC_Assert ( (utf8In != 0) && (utf16Out != 0) && (utf8Read != 0) && (utf16Written != 0) );
+
+ while ( (utf8Left > 0) && (utf16Left > 0) ) {
+
+ // Do a run of ASCII, it copies 1 input unit into 1 output unit.
+ size_t i, limit = utf8Left;
+ if ( limit > utf16Left ) limit = utf16Left;
+ for ( i = 0; i < limit; ++i ) {
+ UTF8Unit inUnit = *utf8Pos;
+ if ( inUnit > 0x7F ) break;
+ *utf16Pos = UTF16Unit(inUnit) << 8; // Better than: UTF16OutSwap ( utf16Pos, inUnit );
+ ++utf8Pos;
+ ++utf16Pos;
+ }
+ utf8Left -= i;
+ utf16Left -= i;
+
+ // Do a run of non-ASCII, it copies multiple input units into 1 or 2 output units.
+ while ( (utf8Left > 0) && (utf16Left > 0) ) {
+ UTF32Unit cp;
+ size_t len8, len16;
+ UTF8Unit inUnit = *utf8Pos;
+ if ( inUnit <= 0x7F ) break;
+ CodePoint_from_UTF8_Multi ( utf8Pos, utf8Left, &cp, &len8 );
+ if ( len8 == 0 ) goto Done; // The input buffer ends in the middle of a character.
+ if ( cp <= 0xFFFF ) {
+ UTF16OutSwap ( utf16Pos, UTF16Unit(cp) );
+ len16 = 1;
+ } else {
+ CodePoint_to_UTF16Swp_Surrogate ( cp, utf16Pos, utf16Left, &len16 );
+ if ( len16 == 0 ) goto Done; // Not enough room in the output buffer.
+ }
+ utf8Left -= len8;
+ utf8Pos += len8;
+ utf16Left -= len16;
+ utf16Pos += len16;
+ }
+
+ }
+
+Done: // Set the output lengths.
+ *utf8Read = utf8Len - utf8Left;
+ *utf16Written = utf16Len - utf16Left;
+
+} // UTF8_to_UTF16Swp
+
+// =================================================================================================
+
+static void UTF8_to_UTF32Swp ( const UTF8Unit * utf8In, const size_t utf8Len,
+ UTF32Unit * utf32Out, const size_t utf32Len,
+ size_t * utf8Read, size_t * utf32Written )
+{
+ const UTF8Unit * utf8Pos = utf8In;
+ UTF32Unit * utf32Pos = utf32Out;
+
+ size_t utf8Left = utf8Len;
+ size_t utf32Left = utf32Len;
+
+ UC_Assert ( (utf8In != 0) && (utf32Out != 0) && (utf8Read != 0) && (utf32Written != 0) );
+
+ while ( (utf8Left > 0) && (utf32Left > 0) ) {
+
+ // Do a run of ASCII, it copies 1 input unit into 1 output unit.
+ size_t i, limit = utf8Left;
+ if ( limit > utf32Left ) limit = utf32Left;
+ for ( i = 0; i < limit; ++i ) {
+ UTF8Unit inUnit = *utf8Pos;
+ if ( inUnit > 0x7F ) break;
+ *utf32Pos = UTF32Unit(inUnit) << 24; // Better than: UTF32OutSwap ( utf32Pos, inUnit );
+ ++utf8Pos;
+ ++utf32Pos;
+ }
+ utf8Left -= i;
+ utf32Left -= i;
+
+ // Do a run of non-ASCII, it copies variable input into 1 output unit.
+ while ( (utf8Left > 0) && (utf32Left > 0) ) {
+ size_t len;
+ UTF32Unit cp;
+ UTF8Unit inUnit = *utf8Pos;
+ if ( inUnit <= 0x7F ) break;
+ CodePoint_from_UTF8_Multi ( utf8Pos, utf8Left, &cp, &len );
+ if ( len == 0 ) goto Done; // The input buffer ends in the middle of a character.
+ UTF32OutSwap ( utf32Pos, cp );
+ utf8Left -= len;
+ utf8Pos += len;
+ utf32Left -= 1;
+ utf32Pos += 1;
+ }
+
+ }
+
+Done: // Set the output lengths.
+ *utf8Read = utf8Len - utf8Left;
+ *utf32Written = utf32Len - utf32Left;
+
+} // UTF8_to_UTF32Swp
+
+// =================================================================================================
+
+static void UTF16Swp_to_UTF8 ( const UTF16Unit * utf16In, const size_t utf16Len,
+ UTF8Unit * utf8Out, const size_t utf8Len,
+ size_t * utf16Read, size_t * utf8Written )
+{
+ const UTF16Unit * utf16Pos = utf16In;
+ UTF8Unit * utf8Pos = utf8Out;
+
+ size_t utf16Left = utf16Len;
+ size_t utf8Left = utf8Len;
+
+ UC_Assert ( (utf16In != 0) && (utf8Out != 0) && (utf16Read != 0) && (utf8Written != 0) );
+
+ while ( (utf16Left > 0) && (utf8Left > 0) ) {
+
+ // Do a run of ASCII, it copies 1 input unit into 1 output unit.
+ size_t i, limit = utf16Left;
+ if ( limit > utf8Left ) limit = utf8Left;
+ for ( i = 0; i < limit; ++i ) {
+ UTF16Unit inUnit = UTF16InSwap(utf16Pos);
+ if ( inUnit > 0x7F ) break;
+ *utf8Pos = UTF8Unit(inUnit);
+ ++utf16Pos;
+ ++utf8Pos;
+ }
+ utf16Left -= i;
+ utf8Left -= i;
+
+ // Do a run of non-ASCII inside the BMP, it copies 1 input unit into multiple output units.
+ while ( (utf16Left > 0) && (utf8Left > 0) ) {
+ size_t len8;
+ UTF16Unit inUnit = UTF16InSwap(utf16Pos);
+ if ( inUnit <= 0x7F ) break;
+ if ( (0xD800 <= inUnit) && (inUnit <= 0xDFFF) ) break;
+ CodePoint_to_UTF8_Multi ( inUnit, utf8Pos, utf8Left, &len8 );
+ if ( len8 == 0 ) goto Done; // Not enough room in the output buffer.
+ utf16Left -= 1;
+ utf16Pos += 1;
+ utf8Left -= len8;
+ utf8Pos += len8;
+ }
+
+ // Do a run of surrogate pairs, it copies 2 input units into multiple output units.
+ while ( (utf16Left > 0) && (utf8Left > 0) ) {
+ UTF32Unit cp;
+ size_t len16, len8;
+ UTF16Unit inUnit = UTF16InSwap(utf16Pos);
+ if ( (inUnit < 0xD800) || (0xDFFF < inUnit) ) break;
+ CodePoint_from_UTF16Swp_Surrogate ( utf16Pos, utf16Left, &cp, &len16 );
+ if ( len16 == 0 ) goto Done; // The input buffer ends in the middle of a surrogate pair.
+ UC_Assert ( len16 == 2 );
+ CodePoint_to_UTF8_Multi ( cp, utf8Pos, utf8Left, &len8 );
+ if ( len8 == 0 ) goto Done; // Not enough room in the output buffer.
+ utf16Left -= len16;
+ utf16Pos += len16;
+ utf8Left -= len8;
+ utf8Pos += len8;
+ }
+
+ }
+
+Done: // Set the output lengths.
+ *utf16Read = utf16Len - utf16Left;
+ *utf8Written = utf8Len - utf8Left;
+
+} // UTF16Swp_to_UTF8
+
+// =================================================================================================
+
+static void UTF32Swp_to_UTF8 ( const UTF32Unit * utf32In, const size_t utf32Len,
+ UTF8Unit * utf8Out, const size_t utf8Len,
+ size_t * utf32Read, size_t * utf8Written )
+{
+ const UTF32Unit * utf32Pos = utf32In;
+ UTF8Unit * utf8Pos = utf8Out;
+
+ size_t utf32Left = utf32Len;
+ size_t utf8Left = utf8Len;
+
+ UC_Assert ( (utf32In != 0) && (utf8Out != 0) && (utf32Read != 0) && (utf8Written != 0) );
+
+ while ( (utf32Left > 0) && (utf8Left > 0) ) {
+
+ // Do a run of ASCII, it copies 1 input unit into 1 output unit.
+ size_t i, limit = utf32Left;
+ if ( limit > utf8Left ) limit = utf8Left;
+ for ( i = 0; i < limit; ++i ) {
+ UTF32Unit cp = UTF32InSwap(utf32Pos);
+ if ( cp > 0x7F ) break;
+ *utf8Pos = UTF8Unit(cp);
+ ++utf32Pos;
+ ++utf8Pos;
+ }
+ utf32Left -= i;
+ utf8Left -= i;
+
+ // Do a run of non-ASCII, it copies 1 input unit into multiple output units.
+ while ( (utf32Left > 0) && (utf8Left > 0) ) {
+ size_t len;
+ UTF32Unit cp = UTF32InSwap(utf32Pos);
+ if ( cp <= 0x7F ) break;
+ CodePoint_to_UTF8_Multi ( cp, utf8Pos, utf8Left, &len );
+ if ( len == 0 ) goto Done; // Not enough room in the output buffer.
+ utf32Left -= 1;
+ utf32Pos += 1;
+ utf8Left -= len;
+ utf8Pos += len;
+ }
+
+ }
+
+Done: // Set the output lengths.
+ *utf32Read = utf32Len - utf32Left;
+ *utf8Written = utf8Len - utf8Left;
+
+} // UTF32Swp_to_UTF8
+
+// =================================================================================================
+
+static void UTF16Swp_to_UTF32Swp ( const UTF16Unit * utf16In, const size_t utf16Len,
+ UTF32Unit * utf32Out, const size_t utf32Len,
+ size_t * utf16Read, size_t * utf32Written )
+{
+ const UTF16Unit * utf16Pos = utf16In;
+ UTF32Unit * utf32Pos = utf32Out;
+
+ size_t utf16Left = utf16Len;
+ size_t utf32Left = utf32Len;
+
+ UC_Assert ( (utf16In != 0) && (utf32Out != 0) && (utf16Read != 0) && (utf32Written != 0) );
+
+ while ( (utf16Left > 0) && (utf32Left > 0) ) {
+
+ // Do a run of BMP, it copies 1 input unit into 1 output unit.
+ size_t i, limit = utf16Left;
+ if ( limit > utf32Left ) limit = utf32Left;
+ for ( i = 0; i < limit; ++i ) {
+ UTF16Unit inUnit = UTF16InSwap(utf16Pos);
+ if ( (0xD800 <= inUnit) && (inUnit <= 0xDFFF) ) break;
+ *utf32Pos = UTF32Unit(*utf16Pos) << 16; // Better than: UTF32OutSwap ( utf32Pos, inUnit );
+ ++utf16Pos;
+ ++utf32Pos;
+ }
+ utf16Left -= i;
+ utf32Left -= i;
+
+ // Do a run of surrogate pairs, it copies 2 input units into 1 output unit.
+ while ( (utf16Left > 0) && (utf32Left > 0) ) {
+ size_t len;
+ UTF32Unit cp;
+ UTF16Unit inUnit = UTF16InSwap(utf16Pos);
+ if ( (inUnit < 0xD800) || (0xDFFF < inUnit) ) break;
+ CodePoint_from_UTF16Swp_Surrogate ( utf16Pos, utf16Left, &cp, &len );
+ if ( len == 0 ) goto Done; // The input buffer ends in the middle of a surrogate pair.
+ UTF32OutSwap ( utf32Pos, cp );
+ UC_Assert ( len == 2 );
+ utf16Left -= len;
+ utf16Pos += len;
+ utf32Left -= 1;
+ utf32Pos += 1;
+ }
+
+ }
+
+Done: // Set the output lengths.
+ *utf16Read = utf16Len - utf16Left;
+ *utf32Written = utf32Len - utf32Left;
+
+} // UTF16Swp_to_UTF32Swp
+
+// =================================================================================================
+
+static void UTF32Swp_to_UTF16Swp ( const UTF32Unit * utf32In, const size_t utf32Len,
+ UTF16Unit * utf16Out, const size_t utf16Len,
+ size_t * utf32Read, size_t * utf16Written )
+{
+ const UTF32Unit * utf32Pos = utf32In;
+ UTF16Unit * utf16Pos = utf16Out;
+
+ size_t utf32Left = utf32Len;
+ size_t utf16Left = utf16Len;
+
+ const size_t k32to16Offset = swap32to16Offset; // ! Make sure compiler treats as an invariant.
+
+ UC_Assert ( (utf32In != 0) && (utf16Out != 0) && (utf32Read != 0) && (utf16Written != 0) );
+
+ while ( (utf32Left > 0) && (utf16Left > 0) ) {
+
+ // Do a run of BMP, it copies 1 input unit into 1 output unit.
+ size_t i, limit = utf32Left;
+ if ( limit > utf16Left ) limit = utf16Left;
+ for ( i = 0; i < limit; ++i ) {
+ UTF32Unit inUnit = UTF32InSwap(utf32Pos);
+ if ( inUnit > 0xFFFF ) break;
+ *utf16Pos = *(((UTF16Unit*)utf32Pos) + k32to16Offset); // Better than: UTF16OutSwap ( utf16Pos, UTF16Unit(inUnit) );
+ ++utf32Pos;
+ ++utf16Pos;
+ }
+ utf32Left -= i;
+ utf16Left -= i;
+
+ // Do a run of non-BMP, it copies 1 input unit into 2 output units.
+ while ( (utf32Left > 0) && (utf16Left > 0) ) {
+ size_t len;
+ UTF32Unit inUnit = UTF32InSwap(utf32Pos);
+ if ( inUnit <= 0xFFFF ) break;
+ CodePoint_to_UTF16Swp_Surrogate ( inUnit, utf16Pos, utf16Left, &len );
+ if ( len == 0 ) goto Done; // Not enough room in the output buffer.
+ UC_Assert ( len == 2 );
+ utf32Left -= 1;
+ utf32Pos += 1;
+ utf16Left -= 2;
+ utf16Pos += 2;
+ }
+
+ }
+
+Done: // Set the output lengths.
+ *utf32Read = utf32Len - utf32Left;
+ *utf16Written = utf16Len - utf16Left;
+
+} // UTF32Swp_to_UTF16Swp
+
+// =================================================================================================
+
+static void UTF16Nat_to_UTF32Swp ( const UTF16Unit * utf16In, const size_t utf16Len,
+ UTF32Unit * utf32Out, const size_t utf32Len,
+ size_t * utf16Read, size_t * utf32Written )
+{
+ const UTF16Unit * utf16Pos = utf16In;
+ UTF32Unit * utf32Pos = utf32Out;
+
+ size_t utf16Left = utf16Len;
+ size_t utf32Left = utf32Len;
+
+ UC_Assert ( (utf16In != 0) && (utf32Out != 0) && (utf16Read != 0) && (utf32Written != 0) );
+
+ while ( (utf16Left > 0) && (utf32Left > 0) ) {
+
+ // Do a run of BMP, it copies 1 input unit into 1 output unit.
+ size_t i, limit = utf16Left;
+ if ( limit > utf32Left ) limit = utf32Left;
+ for ( i = 0; i < limit; ++i ) {
+ UTF16Unit inUnit = *utf16Pos;
+ if ( (0xD800 <= inUnit) && (inUnit <= 0xDFFF) ) break;
+ UTF32OutSwap ( utf32Pos, inUnit );
+ ++utf16Pos;
+ ++utf32Pos;
+ }
+ utf16Left -= i;
+ utf32Left -= i;
+
+ // Do a run of surrogate pairs, it copies 2 input units into 1 output unit.
+ while ( (utf16Left > 0) && (utf32Left > 0) ) {
+ size_t len;
+ UTF32Unit cp;
+ UTF16Unit inUnit = *utf16Pos;
+ if ( (inUnit < 0xD800) || (0xDFFF < inUnit) ) break;
+ CodePoint_from_UTF16Nat_Surrogate ( utf16Pos, utf16Left, &cp, &len );
+ if ( len == 0 ) goto Done; // The input buffer ends in the middle of a surrogate pair.
+ UC_Assert ( len == 2 );
+ UTF32OutSwap ( utf32Pos, cp );
+ utf16Left -= len;
+ utf16Pos += len;
+ utf32Left -= 1;
+ utf32Pos += 1;
+ }
+
+ }
+
+Done: // Set the output lengths.
+ *utf16Read = utf16Len - utf16Left;
+ *utf32Written = utf32Len - utf32Left;
+
+} // UTF16Nat_to_UTF32Swp
+
+// =================================================================================================
+
+static void UTF16Swp_to_UTF32Nat ( const UTF16Unit * utf16In, const size_t utf16Len,
+ UTF32Unit * utf32Out, const size_t utf32Len,
+ size_t * utf16Read, size_t * utf32Written )
+{
+ const UTF16Unit * utf16Pos = utf16In;
+ UTF32Unit * utf32Pos = utf32Out;
+
+ size_t utf16Left = utf16Len;
+ size_t utf32Left = utf32Len;
+
+ UC_Assert ( (utf16In != 0) && (utf32Out != 0) && (utf16Read != 0) && (utf32Written != 0) );
+
+ while ( (utf16Left > 0) && (utf32Left > 0) ) {
+
+ // Do a run of BMP, it copies 1 input unit into 1 output unit.
+ size_t i, limit = utf16Left;
+ if ( limit > utf32Left ) limit = utf32Left;
+ for ( i = 0; i < limit; ++i ) {
+ UTF16Unit inUnit = UTF16InSwap(utf16Pos);
+ if ( (0xD800 <= inUnit) && (inUnit <= 0xDFFF) ) break;
+ *utf32Pos = inUnit;
+ ++utf16Pos;
+ ++utf32Pos;
+ }
+ utf16Left -= i;
+ utf32Left -= i;
+
+ // Do a run of surrogate pairs, it copies 2 input units into 1 output unit.
+ while ( (utf16Left > 0) && (utf32Left > 0) ) {
+ size_t len;
+ UTF16Unit inUnit = UTF16InSwap(utf16Pos);
+ if ( (inUnit < 0xD800) || (0xDFFF < inUnit) ) break;
+ CodePoint_from_UTF16Swp_Surrogate ( utf16Pos, utf16Left, utf32Pos, &len );
+ if ( len == 0 ) goto Done; // The input buffer ends in the middle of a surrogate pair.
+ UC_Assert ( len == 2 );
+ utf16Left -= len;
+ utf16Pos += len;
+ utf32Left -= 1;
+ utf32Pos += 1;
+ }
+
+ }
+
+Done: // Set the output lengths.
+ *utf16Read = utf16Len - utf16Left;
+ *utf32Written = utf32Len - utf32Left;
+
+} // UTF16Swp_to_UTF32Nat
+
+// =================================================================================================
+
+static void UTF32Nat_to_UTF16Swp ( const UTF32Unit * utf32In, const size_t utf32Len,
+ UTF16Unit * utf16Out, const size_t utf16Len,
+ size_t * utf32Read, size_t * utf16Written )
+{
+ const UTF32Unit * utf32Pos = utf32In;
+ UTF16Unit * utf16Pos = utf16Out;
+
+ size_t utf32Left = utf32Len;
+ size_t utf16Left = utf16Len;
+
+ UC_Assert ( (utf32In != 0) && (utf16Out != 0) && (utf32Read != 0) && (utf16Written != 0) );
+
+ while ( (utf32Left > 0) && (utf16Left > 0) ) {
+
+ // Do a run of BMP, it copies 1 input unit into 1 output unit.
+ size_t i, limit = utf32Left;
+ if ( limit > utf16Left ) limit = utf16Left;
+ for ( i = 0; i < limit; ++i ) {
+ UTF32Unit inUnit = *utf32Pos;
+ if ( inUnit > 0xFFFF ) break;
+ UTF16OutSwap ( utf16Pos, UTF16Unit(inUnit) );
+ ++utf32Pos;
+ ++utf16Pos;
+ }
+ utf32Left -= i;
+ utf16Left -= i;
+
+ // Do a run of non-BMP, it copies 1 input unit into 2 output units.
+ while ( (utf32Left > 0) && (utf16Left > 0) ) {
+ size_t len;
+ UTF32Unit inUnit = *utf32Pos;
+ if ( inUnit <= 0xFFFF ) break;
+ CodePoint_to_UTF16Swp_Surrogate ( inUnit, utf16Pos, utf16Left, &len );
+ if ( len == 0 ) goto Done; // Not enough room in the output buffer.
+ UC_Assert ( len == 2 );
+ utf32Left -= 1;
+ utf32Pos += 1;
+ utf16Left -= 2;
+ utf16Pos += 2;
+ }
+
+ }
+
+Done: // Set the output lengths.
+ *utf32Read = utf32Len - utf32Left;
+ *utf16Written = utf16Len - utf16Left;
+
+} // UTF32Nat_to_UTF16Swp
+
+// =================================================================================================
+
+static void UTF32Swp_to_UTF16Nat ( const UTF32Unit * utf32In, const size_t utf32Len,
+ UTF16Unit * utf16Out, const size_t utf16Len,
+ size_t * utf32Read, size_t * utf16Written )
+{
+ const UTF32Unit * utf32Pos = utf32In;
+ UTF16Unit * utf16Pos = utf16Out;
+
+ size_t utf32Left = utf32Len;
+ size_t utf16Left = utf16Len;
+
+ UC_Assert ( (utf32In != 0) && (utf16Out != 0) && (utf32Read != 0) && (utf16Written != 0) );
+
+ while ( (utf32Left > 0) && (utf16Left > 0) ) {
+
+ // Do a run of BMP, it copies 1 input unit into 1 output unit.
+ size_t i, limit = utf32Left;
+ if ( limit > utf16Left ) limit = utf16Left;
+ for ( i = 0; i < limit; ++i ) {
+ UTF32Unit inUnit = UTF32InSwap(utf32Pos);
+ if ( inUnit > 0xFFFF ) break;
+ *utf16Pos = UTF16Unit(inUnit);
+ ++utf32Pos;
+ ++utf16Pos;
+ }
+ utf32Left -= i;
+ utf16Left -= i;
+
+ // Do a run of non-BMP, it copies 1 input unit into 2 output units.
+ while ( (utf32Left > 0) && (utf16Left > 0) ) {
+ size_t len;
+ UTF32Unit inUnit = UTF32InSwap(utf32Pos);
+ if ( inUnit <= 0xFFFF ) break;
+ CodePoint_to_UTF16Nat_Surrogate ( inUnit, utf16Pos, utf16Left, &len );
+ if ( len == 0 ) goto Done; // Not enough room in the output buffer.
+ UC_Assert ( len == 2 );
+ utf32Left -= 1;
+ utf32Pos += 1;
+ utf16Left -= 2;
+ utf16Pos += 2;
+ }
+
+ }
+
+Done: // Set the output lengths.
+ *utf32Read = utf32Len - utf32Left;
+ *utf16Written = utf16Len - utf16Left;
+
+} // UTF32Swp_to_UTF16Nat
+
+// =================================================================================================
diff --git a/gpr/source/lib/xmp_core/UnicodeConversions.hpp b/gpr/source/lib/xmp_core/UnicodeConversions.hpp
new file mode 100644
index 0000000..f09437c
--- /dev/null
+++ b/gpr/source/lib/xmp_core/UnicodeConversions.hpp
@@ -0,0 +1,115 @@
+#ifndef __UnicodeConversions_h__
+#define __UnicodeConversions_h__
+
+// =================================================================================================
+// Copyright 2004 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.
+// =================================================================================================
+
+#include <string>
+
+// =================================================================================================
+
+typedef XMP_Uns8 UTF8Unit;
+typedef XMP_Uns16 UTF16Unit;
+typedef XMP_Uns32 UTF32Unit;
+
+// -------------------------------------------------------------------------------------------------
+
+// ! The UTF16 and UTF32 counts are in storage units, not bytes! CodePoint values are always native.
+
+// *** MIght be better to return a status than throw an exception for errors?
+
+typedef void (*CodePoint_to_UTF16_Proc) ( const UTF32Unit cpIn, UTF16Unit * utf16Out, const size_t utf16Len, size_t * utf16Written );
+
+typedef void (*CodePoint_from_UTF16_Proc) ( const UTF16Unit * utf16In, const size_t utf16Len, UTF32Unit * cpOut, size_t * utf16Read );
+
+typedef void (*UTF8_to_UTF16_Proc) ( const UTF8Unit * utf8In, const size_t utf8Len,
+ UTF16Unit * utf16Out, const size_t utf16Len,
+ size_t * utf8Read, size_t * utf16Written );
+
+typedef void (*UTF8_to_UTF32_Proc) ( const UTF8Unit * utf8In, const size_t utf8Len,
+ UTF32Unit * utf32Out, const size_t utf32Len,
+ size_t * utf8Read, size_t * utf32Written );
+
+typedef void (*UTF16_to_UTF8_Proc) ( const UTF16Unit * utf16In, const size_t utf16Len,
+ UTF8Unit * utf8Out, const size_t utf8Len,
+ size_t * utf16Read, size_t * utf8Written );
+
+typedef void (*UTF32_to_UTF8_Proc) ( const UTF32Unit * utf32In, const size_t utf32Len,
+ UTF8Unit * utf8Out, const size_t utf8Len,
+ size_t * utf32Read, size_t * utf8Written );
+
+typedef void (*UTF16_to_UTF32_Proc) ( const UTF16Unit * utf16In, const size_t utf16Len,
+ UTF32Unit * utf32Out, const size_t utf32Len,
+ size_t * utf16Read, size_t * utf32Written );
+
+typedef void (*UTF32_to_UTF16_Proc) ( const UTF32Unit * utf32In, const size_t utf32Len,
+ UTF16Unit * utf16Out, const size_t utf16Len,
+ size_t * utf32Read, size_t * utf16Written );
+
+// -------------------------------------------------------------------------------------------------
+
+extern void CodePoint_to_UTF8 ( const UTF32Unit cpIn, UTF8Unit * utf8Out, const size_t utf8Len, size_t * utf8Written );
+
+extern void CodePoint_from_UTF8 ( const UTF8Unit * utf8In, const size_t utf8Len, UTF32Unit * cpOut, size_t * utf8Read );
+
+extern CodePoint_to_UTF16_Proc CodePoint_to_UTF16BE;
+extern CodePoint_to_UTF16_Proc CodePoint_to_UTF16LE;
+
+extern CodePoint_from_UTF16_Proc CodePoint_from_UTF16BE;
+extern CodePoint_from_UTF16_Proc CodePoint_from_UTF16LE;
+
+extern UTF8_to_UTF16_Proc UTF8_to_UTF16BE;
+extern UTF8_to_UTF16_Proc UTF8_to_UTF16LE;
+
+extern UTF8_to_UTF32_Proc UTF8_to_UTF32BE;
+extern UTF8_to_UTF32_Proc UTF8_to_UTF32LE;
+
+extern UTF16_to_UTF8_Proc UTF16BE_to_UTF8;
+extern UTF16_to_UTF8_Proc UTF16LE_to_UTF8;
+
+extern UTF32_to_UTF8_Proc UTF32BE_to_UTF8;
+extern UTF32_to_UTF8_Proc UTF32LE_to_UTF8;
+
+extern UTF8_to_UTF16_Proc UTF8_to_UTF16Native;
+extern UTF8_to_UTF32_Proc UTF8_to_UTF32Native;
+
+extern UTF16_to_UTF8_Proc UTF16Native_to_UTF8;
+extern UTF32_to_UTF8_Proc UTF32Native_to_UTF8;
+
+extern UTF16_to_UTF32_Proc UTF16BE_to_UTF32BE;
+extern UTF16_to_UTF32_Proc UTF16BE_to_UTF32LE;
+
+extern UTF16_to_UTF32_Proc UTF16LE_to_UTF32BE;
+extern UTF16_to_UTF32_Proc UTF16LE_to_UTF32LE;
+
+extern UTF32_to_UTF16_Proc UTF32BE_to_UTF16BE;
+extern UTF32_to_UTF16_Proc UTF32BE_to_UTF16LE;
+
+extern UTF32_to_UTF16_Proc UTF32LE_to_UTF16BE;
+extern UTF32_to_UTF16_Proc UTF32LE_to_UTF16LE;
+
+extern void SwapUTF16 ( const UTF16Unit * utf16In, UTF16Unit * utf16Out, const size_t utf16Len );
+extern void SwapUTF32 ( const UTF32Unit * utf32In, UTF32Unit * utf32Out, const size_t utf32Len );
+
+extern void ToUTF16 ( const UTF8Unit * utf8In, size_t utf8Len, std::string * utf16Str, bool bigEndian );
+extern void ToUTF32 ( const UTF8Unit * utf8In, size_t utf8Len, std::string * utf32Str, bool bigEndian );
+
+extern void FromUTF16 ( const UTF16Unit * utf16In, size_t utf16Len, std::string * utf8Str, bool bigEndian );
+extern void FromUTF32 ( const UTF32Unit * utf32In, size_t utf32Len, std::string * utf8Str, bool bigEndian );
+
+extern void ToUTF16Native ( const UTF8Unit * utf8In, size_t utf8Len, std::string * utf16Str );
+extern void ToUTF32Native ( const UTF8Unit * utf8In, size_t utf8Len, std::string * utf32Str );
+
+extern void FromUTF16Native ( const UTF16Unit * utf16In, size_t utf16Len, std::string * utf8Str );
+extern void FromUTF32Native ( const UTF32Unit * utf32In, size_t utf32Len, std::string * utf8Str );
+
+extern void InitializeUnicodeConversions();
+
+// =================================================================================================
+
+#endif // __UnicodeConversions_h__
diff --git a/gpr/source/lib/xmp_core/UnicodeInlines.incl_cpp b/gpr/source/lib/xmp_core/UnicodeInlines.incl_cpp
new file mode 100644
index 0000000..d96d370
--- /dev/null
+++ b/gpr/source/lib/xmp_core/UnicodeInlines.incl_cpp
@@ -0,0 +1,129 @@
+#ifndef __UnicodeInlines_incl_cpp__
+#define __UnicodeInlines_incl_cpp__
+
+// =================================================================================================
+// Copyright 2004 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.
+// =================================================================================================
+
+#include "UnicodeConversions.hpp"
+
+// =================================================================================================
+// Inner loop utilities that need to be inlined.
+// =================================================================================================
+
+static inline XMP_Uns32 GetCodePoint ( const XMP_Uns8 ** utf8Str_io )
+{
+ const XMP_Uns8 * u8Ptr = *utf8Str_io;
+ XMP_Uns32 cp;
+ size_t u8Len;
+ CodePoint_from_UTF8 ( u8Ptr, 4, &cp, &u8Len ); // Throws an exception for errors.
+ *utf8Str_io = u8Ptr + u8Len;
+ return cp;
+}
+
+// =================================================================================================
+
+static inline bool IsStartChar_ASCII ( XMP_Uns32 cp )
+{
+ // ASCII starting characters for an XML name.
+ if ( (('a' <= cp) && (cp <= 'z')) || (('A' <= cp) && (cp <= 'Z')) || (cp == '_') ) return true;
+ return false;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+static inline bool IsStartChar_NonASCII ( XMP_Uns32 cp )
+{
+ // Non-ASCII starting characters for an XML name.
+
+ if ( ((0xC0 <= cp) && (cp <= 0xD6)) || ((0xD8 <= cp) && (cp <= 0xF6)) ) return true;
+ if ( ((0xF8 <= cp) && (cp <= 0x2FF)) || ((0x370 <= cp) && (cp <= 0x37D)) ) return true;
+
+ if ( ((0x37F <= cp) && (cp <= 0x1FFF)) || ((0x200C <= cp) && (cp <= 0x200D)) ) return true;
+ if ( ((0x2070 <= cp) && (cp <= 0x218F)) || ((0x2C00 <= cp) && (cp <= 0x2FEF)) ) return true;
+ if ( ((0x3001 <= cp) && (cp <= 0xD7FF)) || ((0xF900 <= cp) && (cp <= 0xFDCF)) ) return true;
+ if ( ((0xFDF0 <= cp) && (cp <= 0xFFFD)) || ((0x10000 <= cp) && (cp <= 0xEFFFF)) ) return true;
+
+ return false;
+
+}
+
+// -------------------------------------------------------------------------------------------------
+
+static inline bool IsOtherChar_ASCII ( XMP_Uns32 cp )
+{
+ // ASCII following characters for an XML name.
+ if ( (('0' <= cp) && (cp <= '9')) || (cp == '-') || (cp == '.') ) return true;
+ return false;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+static inline bool IsOtherChar_NonASCII ( XMP_Uns32 cp )
+{
+ // Non-ASCII following characters for an XML name.
+ if ( (cp == 0xB7) || ((0x300 <= cp) && (cp <= 0x36F)) || ((0x203F <= cp) && (cp <= 0x2040)) ) return true;
+ return false;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+static inline void VerifyUTF8 ( XMP_StringPtr str )
+{
+ const XMP_Uns8 * utf8Str = (XMP_Uns8*)str;
+ while ( *utf8Str != 0 ) {
+ while ( (*utf8Str != 0) && (*utf8Str < 0x80) ) ++utf8Str;
+ if ( *utf8Str >= 0x80 ) (void) GetCodePoint ( &utf8Str ); // Throws for bad UTF-8.
+ }
+}
+
+// -------------------------------------------------------------------------------------------------
+
+static inline void VerifySimpleXMLName ( XMP_StringPtr _nameStart, XMP_StringPtr _nameEnd )
+{
+
+ const XMP_Uns8 * nameStart = (const XMP_Uns8 *) _nameStart;
+ const XMP_Uns8 * nameEnd = (const XMP_Uns8 *) _nameEnd;
+ const XMP_Uns8 * namePos = nameStart;
+ XMP_Uns32 cp;
+
+ // The first character is more restricted.
+
+ if ( nameStart >= nameEnd ) XMP_Throw ( "Empty XML name", kXMPErr_BadXPath );
+
+ cp = *namePos;
+ if ( cp < 0x80 ) {
+ ++namePos;
+ if ( ! IsStartChar_ASCII(cp) ) goto NameError;
+ } else {
+ cp = GetCodePoint ( &namePos );
+ if ( ! IsStartChar_NonASCII(cp) ) goto NameError;
+ }
+
+ // Check the rest of the name.
+
+ while ( namePos < nameEnd ) {
+ cp = *namePos;
+ if ( cp < 0x80 ) {
+ ++namePos;
+ if ( (! IsStartChar_ASCII(cp)) && (! IsOtherChar_ASCII(cp)) ) goto NameError;
+ } else {
+ cp = GetCodePoint ( &namePos );
+ if ( (! IsStartChar_NonASCII(cp)) && (! IsOtherChar_NonASCII(cp)) ) goto NameError;
+ }
+ }
+
+ return;
+
+NameError:
+ XMP_Throw ( "Bad XML name", kXMPErr_BadXPath );
+
+} // VerifySimpleXMLName
+
+// =================================================================================================
+
+#endif // __UnicodeInlines_incl_cpp__
diff --git a/gpr/source/lib/xmp_core/WXMPIterator.cpp b/gpr/source/lib/xmp_core/WXMPIterator.cpp
new file mode 100644
index 0000000..f96323f
--- /dev/null
+++ b/gpr/source/lib/xmp_core/WXMPIterator.cpp
@@ -0,0 +1,170 @@
+// =================================================================================================
+// Copyright 2004 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.
+// =================================================================================================
+
+#include "public/include/XMP_Environment.h" // ! This must be the first include!
+#include "public/include/XMP_Const.h"
+
+#include "public/include/client-glue/WXMPIterator.hpp"
+
+#include "XMPCore_Impl.hpp"
+#include "XMPIterator.hpp"
+
+#if XMP_WinBuild
+ #pragma warning ( disable : 4101 ) // unreferenced local variable
+ #pragma warning ( disable : 4189 ) // local variable is initialized but not referenced
+ #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning)
+ #if XMP_DebugBuild
+ #pragma warning ( disable : 4297 ) // function assumed not to throw an exception but does
+ #endif
+#endif
+
+#if __cplusplus
+extern "C" {
+#endif
+
+// =================================================================================================
+// CTor/DTor Wrappers
+// ==================
+
+void
+WXMPIterator_PropCTor_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_OptionBits options,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPIterator_PropCTor_1" ) // No lib object yet, use the static entry.
+
+ if ( schemaNS == 0 ) schemaNS = "";
+ if ( propName == 0 ) propName = "";
+
+ const XMPMeta & xmpObj = WtoXMPMeta_Ref ( xmpRef );
+ XMP_AutoLock metaLock ( &xmpObj.lock, kXMP_ReadLock );
+
+ XMPIterator * iter = new XMPIterator ( xmpObj, schemaNS, propName, options );
+ ++iter->clientRefs;
+ XMP_Assert ( iter->clientRefs == 1 );
+ wResult->ptrResult = XMPIteratorRef ( iter );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPIterator_TableCTor_1 ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_OptionBits options,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPIterator_TableCTor_1" ) // No lib object yet, use the static entry.
+
+ if ( schemaNS == 0 ) schemaNS = "";
+ if ( propName == 0 ) propName = "";
+
+ XMPIterator * iter = new XMPIterator ( schemaNS, propName, options );
+ ++iter->clientRefs;
+ XMP_Assert ( iter->clientRefs == 1 );
+ wResult->ptrResult = XMPIteratorRef ( iter );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPIterator_IncrementRefCount_1 ( XMPIteratorRef xmpObjRef )
+{
+ WXMP_Result * wResult = &void_wResult; // ! Needed to "fool" the EnterWrapper macro.
+ XMP_ENTER_ObjWrite ( XMPIterator, "WXMPIterator_IncrementRefCount_1" )
+
+ ++thiz->clientRefs;
+ XMP_Assert ( thiz->clientRefs > 1 );
+
+ XMP_EXIT_NoThrow
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPIterator_DecrementRefCount_1 ( XMPIteratorRef xmpObjRef )
+{
+ WXMP_Result * wResult = &void_wResult; // ! Needed to "fool" the EnterWrapper macro.
+ XMP_ENTER_ObjWrite ( XMPIterator, "WXMPIterator_DecrementRefCount_1" )
+
+ XMP_Assert ( thiz->clientRefs > 0 );
+ --thiz->clientRefs;
+ if ( thiz->clientRefs <= 0 ) {
+ objLock.Release();
+ delete ( thiz );
+ }
+
+ XMP_EXIT_NoThrow
+}
+
+// =================================================================================================
+// Class Method Wrappers
+// =====================
+
+void
+WXMPIterator_Next_1 ( XMPIteratorRef xmpObjRef,
+ void * schemaNS,
+ void * propPath,
+ void * propValue,
+ XMP_OptionBits * propOptions,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPIterator, "WXMPIterator_Next_1" )
+
+ XMP_StringPtr schemaPtr = 0;
+ XMP_StringLen schemaLen = 0;
+ XMP_StringPtr pathPtr = 0;
+ XMP_StringLen pathLen = 0;
+ XMP_StringPtr valuePtr = 0;
+ XMP_StringLen valueLen = 0;
+
+ if ( propOptions == 0 ) propOptions = &voidOptionBits;
+
+ XMP_Assert( thiz->info.xmpObj != NULL );
+ XMP_AutoLock metaLock ( &thiz->info.xmpObj->lock, kXMP_ReadLock, (thiz->info.xmpObj != 0) );
+
+ XMP_Bool found = thiz->Next ( &schemaPtr, &schemaLen, &pathPtr, &pathLen, &valuePtr, &valueLen, propOptions );
+ wResult->int32Result = found;
+
+ if ( found ) {
+ if ( schemaNS != 0 ) (*SetClientString) ( schemaNS, schemaPtr, schemaLen );
+ if ( propPath != 0 ) (*SetClientString) ( propPath, pathPtr, pathLen );
+ if ( propValue != 0 ) (*SetClientString) ( propValue, valuePtr, valueLen );
+ }
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPIterator_Skip_1 ( XMPIteratorRef xmpObjRef,
+ XMP_OptionBits options,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPIterator, "WXMPIterator_Skip_1" )
+
+ XMP_Assert( thiz->info.xmpObj != NULL );
+ XMP_AutoLock metaLock ( &thiz->info.xmpObj->lock, kXMP_ReadLock, (thiz->info.xmpObj != 0) );
+
+ thiz->Skip ( options );
+
+ XMP_EXIT
+}
+
+// =================================================================================================
+
+#if __cplusplus
+} /* extern "C" */
+#endif
diff --git a/gpr/source/lib/xmp_core/WXMPMeta.cpp b/gpr/source/lib/xmp_core/WXMPMeta.cpp
new file mode 100644
index 0000000..956f08f
--- /dev/null
+++ b/gpr/source/lib/xmp_core/WXMPMeta.cpp
@@ -0,0 +1,1191 @@
+// =================================================================================================
+// Copyright 2004 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.
+// =================================================================================================
+
+#include "public/include/XMP_Environment.h" // ! This must be the first include!
+#include "public/include/XMP_Const.h"
+
+#include "public/include/client-glue/WXMPMeta.hpp"
+
+#include "XMPCore_Impl.hpp"
+#include "XMPMeta.hpp"
+
+#if XMP_WinBuild
+ #pragma warning ( disable : 4101 ) // unreferenced local variable
+ #pragma warning ( disable : 4189 ) // local variable is initialized but not referenced
+ #pragma warning ( disable : 4702 ) // unreachable code
+ #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning)
+ #if XMP_DebugBuild
+ #pragma warning ( disable : 4297 ) // function assumed not to throw an exception but does
+ #endif
+#endif
+
+#if __cplusplus
+extern "C" {
+#endif
+
+// =================================================================================================
+// Init/Term Wrappers
+// ==================
+
+/* class static */ void
+WXMPMeta_GetVersionInfo_1 ( XMP_VersionInfo * info )
+{
+ WXMP_Result * wResult = &void_wResult; // ! Needed to "fool" the EnterWrapper macro.
+ XMP_ENTER_NoLock ( "WXMPMeta_GetVersionInfo_1" )
+
+ XMPMeta::GetVersionInfo ( info );
+
+ XMP_EXIT_NoThrow
+}
+
+// -------------------------------------------------------------------------------------------------
+
+/* class static */ void
+WXMPMeta_Initialize_1 ( WXMP_Result * wResult )
+{
+ XMP_ENTER_NoLock ( "WXMPMeta_Initialize_1" )
+
+ wResult->int32Result = XMPMeta::Initialize();
+
+ XMP_EXIT
+}
+// -------------------------------------------------------------------------------------------------
+
+/* class static */ void
+WXMPMeta_Terminate_1()
+{
+ WXMP_Result * wResult = &void_wResult; // ! Needed to "fool" the EnterWrapper macro.
+ XMP_ENTER_NoLock ( "WXMPMeta_Terminate_1" )
+
+ XMPMeta::Terminate();
+
+ XMP_EXIT_NoThrow
+}
+
+// =================================================================================================
+// CTor/DTor Wrappers
+// ==================
+
+void
+WXMPMeta_CTor_1 ( WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPMeta_CTor_1" ) // No lib object yet, use the static entry.
+
+ XMPMeta * xmpObj = new XMPMeta();
+ ++xmpObj->clientRefs;
+ XMP_Assert ( xmpObj->clientRefs == 1 );
+ wResult->ptrResult = XMPMetaRef ( xmpObj );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_IncrementRefCount_1 ( XMPMetaRef xmpObjRef )
+{
+ WXMP_Result * wResult = &void_wResult; // ! Needed to "fool" the EnterWrapper macro.
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_IncrementRefCount_1" )
+
+ ++thiz->clientRefs;
+ XMP_Assert ( thiz->clientRefs > 0 );
+
+ XMP_EXIT_NoThrow
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_DecrementRefCount_1 ( XMPMetaRef xmpObjRef )
+{
+ WXMP_Result * wResult = &void_wResult; // ! Needed to "fool" the EnterWrapper macro.
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_DecrementRefCount_1" )
+
+ XMP_Assert ( thiz->clientRefs > 0 );
+ --thiz->clientRefs;
+ if ( thiz->clientRefs <= 0 ) {
+ objLock.Release();
+ delete ( thiz );
+ }
+
+ XMP_EXIT_NoThrow
+}
+
+// =================================================================================================
+// Class Static Wrappers
+// =====================
+
+/* class static */ void
+WXMPMeta_GetGlobalOptions_1 ( WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPMeta_GetGlobalOptions_1" )
+
+ XMP_OptionBits options = XMPMeta::GetGlobalOptions();
+ wResult->int32Result = options;
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+/* class static */ void
+WXMPMeta_SetGlobalOptions_1 ( XMP_OptionBits options,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPMeta_SetGlobalOptions_1" )
+
+ XMPMeta::SetGlobalOptions ( options );
+
+ XMP_EXIT
+}
+// -------------------------------------------------------------------------------------------------
+
+/* class static */ void
+WXMPMeta_DumpNamespaces_1 ( XMP_TextOutputProc outProc,
+ void * refCon,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPMeta_DumpNamespaces_1" )
+
+ if ( outProc == 0 ) XMP_Throw ( "Null client output routine", kXMPErr_BadParam );
+
+ XMP_Status status = XMPMeta::DumpNamespaces ( outProc, refCon );
+ wResult->int32Result = status;
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+/* class static */ void
+WXMPMeta_RegisterNamespace_1 ( XMP_StringPtr namespaceURI,
+ XMP_StringPtr suggestedPrefix,
+ void * actualPrefix,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPMeta_RegisterNamespace_1" )
+
+ if ( (namespaceURI == 0) || (*namespaceURI == 0) ) XMP_Throw ( "Empty namespace URI", kXMPErr_BadSchema );
+ if ( (suggestedPrefix == 0) || (*suggestedPrefix == 0) ) XMP_Throw ( "Empty suggested prefix", kXMPErr_BadSchema );
+
+ XMP_StringPtr prefixPtr = 0;
+ XMP_StringLen prefixSize = 0;
+
+ bool prefixMatch = XMPMeta::RegisterNamespace ( namespaceURI, suggestedPrefix, &prefixPtr, &prefixSize );
+ wResult->int32Result = prefixMatch;
+
+ if ( actualPrefix != 0 ) (*SetClientString) ( actualPrefix, prefixPtr, prefixSize );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+/* class static */ void
+WXMPMeta_GetNamespacePrefix_1 ( XMP_StringPtr namespaceURI,
+ void * namespacePrefix,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPMeta_GetNamespacePrefix_1" )
+
+ if ( (namespaceURI == 0) || (*namespaceURI == 0) ) XMP_Throw ( "Empty namespace URI", kXMPErr_BadSchema );
+
+ XMP_StringPtr prefixPtr = 0;
+ XMP_StringLen prefixSize = 0;
+
+ bool found = XMPMeta::GetNamespacePrefix ( namespaceURI, &prefixPtr, &prefixSize );
+ wResult->int32Result = found;
+
+ if ( found && (namespacePrefix != 0) ) (*SetClientString) ( namespacePrefix, prefixPtr, prefixSize );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+/* class static */ void
+WXMPMeta_GetNamespaceURI_1 ( XMP_StringPtr namespacePrefix,
+ void * namespaceURI,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPMeta_GetNamespaceURI_1" )
+
+ if ( (namespacePrefix == 0) || (*namespacePrefix == 0) ) XMP_Throw ( "Empty namespace prefix", kXMPErr_BadSchema );
+
+ XMP_StringPtr uriPtr = 0;
+ XMP_StringLen uriSize = 0;
+
+ bool found = XMPMeta::GetNamespaceURI ( namespacePrefix, &uriPtr, &uriSize );
+ wResult->int32Result = found;
+
+ if ( found && (namespaceURI != 0) ) (*SetClientString) ( namespaceURI, uriPtr, uriSize );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+/* class static */ void
+WXMPMeta_DeleteNamespace_1 ( XMP_StringPtr namespaceURI,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPMeta_DeleteNamespace_1" )
+
+ if ( (namespaceURI == 0) || (*namespaceURI == 0) ) XMP_Throw ( "Empty namespace URI", kXMPErr_BadSchema );
+
+ XMPMeta::DeleteNamespace ( namespaceURI );
+
+ XMP_EXIT
+}
+
+// =================================================================================================
+// Class Method Wrappers
+// =====================
+
+void
+WXMPMeta_GetProperty_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ void * propValue,
+ XMP_OptionBits * options,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult ) /* const */
+{
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetProperty_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
+
+ XMP_StringPtr valuePtr = 0;
+ XMP_StringLen valueSize = 0;
+ if ( options == 0 ) options = &voidOptionBits;
+
+ bool found = thiz.GetProperty ( schemaNS, propName, &valuePtr, &valueSize, options );
+ wResult->int32Result = found;
+
+ if ( found && (propValue != 0) ) (*SetClientString) ( propValue, valuePtr, valueSize );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_GetArrayItem_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex,
+ void * itemValue,
+ XMP_OptionBits * options,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult ) /* const */
+{
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetArrayItem_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
+
+ XMP_StringPtr valuePtr = 0;
+ XMP_StringLen valueSize = 0;
+ if ( options == 0 ) options = &voidOptionBits;
+
+ bool found = thiz.GetArrayItem ( schemaNS, arrayName, itemIndex, &valuePtr, &valueSize, options );
+ wResult->int32Result = found;
+
+ if ( found && (itemValue != 0) ) (*SetClientString) ( itemValue, valuePtr, valueSize );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_GetStructField_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ void * fieldValue,
+ XMP_OptionBits * options,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult ) /* const */
+{
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetStructField_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (structName == 0) || (*structName == 0) ) XMP_Throw ( "Empty struct name", kXMPErr_BadXPath );
+ if ( (fieldNS == 0) || (*fieldNS == 0) ) XMP_Throw ( "Empty field namespace URI", kXMPErr_BadSchema );
+ if ( (fieldName == 0) || (*fieldName == 0) ) XMP_Throw ( "Empty field name", kXMPErr_BadXPath );
+
+ XMP_StringPtr valuePtr = 0;
+ XMP_StringLen valueSize = 0;
+ if ( options == 0 ) options = &voidOptionBits;
+
+ bool found = thiz.GetStructField ( schemaNS, structName, fieldNS, fieldName, &valuePtr, &valueSize, options );
+ wResult->int32Result = found;
+
+ if ( found && (fieldValue != 0) ) (*SetClientString) ( fieldValue, valuePtr, valueSize );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_GetQualifier_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName,
+ void * qualValue,
+ XMP_OptionBits * options,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult ) /* const */
+{
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetQualifier_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
+ if ( (qualNS == 0) || (*qualNS == 0) ) XMP_Throw ( "Empty qualifier namespace URI", kXMPErr_BadSchema );
+ if ( (qualName == 0) || (*qualName == 0) ) XMP_Throw ( "Empty qualifier name", kXMPErr_BadXPath );
+
+ XMP_StringPtr valuePtr = 0;
+ XMP_StringLen valueSize = 0;
+ if ( options == 0 ) options = &voidOptionBits;
+
+ bool found = thiz.GetQualifier ( schemaNS, propName, qualNS, qualName, &valuePtr, &valueSize, options );
+ wResult->int32Result = found;
+
+ if ( found && (qualValue != 0) ) (*SetClientString) ( qualValue, valuePtr, valueSize );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_SetProperty_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr propValue,
+ XMP_OptionBits options,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetProperty_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
+
+ thiz->SetProperty ( schemaNS, propName, propValue, options );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_SetArrayItem_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex,
+ XMP_StringPtr itemValue,
+ XMP_OptionBits options,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetArrayItem_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
+
+ thiz->SetArrayItem ( schemaNS, arrayName, itemIndex, itemValue, options );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_AppendArrayItem_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_OptionBits arrayOptions,
+ XMP_StringPtr itemValue,
+ XMP_OptionBits options,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_AppendArrayItem_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
+
+ thiz->AppendArrayItem ( schemaNS, arrayName, arrayOptions, itemValue, options );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_SetStructField_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ XMP_StringPtr fieldValue,
+ XMP_OptionBits options,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetStructField_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (structName == 0) || (*structName == 0) ) XMP_Throw ( "Empty struct name", kXMPErr_BadXPath );
+ if ( (fieldNS == 0) || (*fieldNS == 0) ) XMP_Throw ( "Empty field namespace URI", kXMPErr_BadSchema );
+ if ( (fieldName == 0) || (*fieldName == 0) ) XMP_Throw ( "Empty field name", kXMPErr_BadXPath );
+
+ thiz->SetStructField ( schemaNS, structName, fieldNS, fieldName, fieldValue, options );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_SetQualifier_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName,
+ XMP_StringPtr qualValue,
+ XMP_OptionBits options,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetQualifier_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
+ if ( (qualNS == 0) || (*qualNS == 0) ) XMP_Throw ( "Empty qualifier namespace URI", kXMPErr_BadSchema );
+ if ( (qualName == 0) || (*qualName == 0) ) XMP_Throw ( "Empty qualifier name", kXMPErr_BadXPath );
+
+ thiz->SetQualifier ( schemaNS, propName, qualNS, qualName, qualValue, options );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_DeleteProperty_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_DeleteProperty_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
+
+ thiz->DeleteProperty ( schemaNS, propName );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_DeleteArrayItem_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_DeleteArrayItem_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
+
+ thiz->DeleteArrayItem ( schemaNS, arrayName, itemIndex );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_DeleteStructField_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_DeleteStructField_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (structName == 0) || (*structName == 0) ) XMP_Throw ( "Empty struct name", kXMPErr_BadXPath );
+ if ( (fieldNS == 0) || (*fieldNS == 0) ) XMP_Throw ( "Empty field namespace URI", kXMPErr_BadSchema );
+ if ( (fieldName == 0) || (*fieldName == 0) ) XMP_Throw ( "Empty field name", kXMPErr_BadXPath );
+
+ thiz->DeleteStructField ( schemaNS, structName, fieldNS, fieldName );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_DeleteQualifier_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_DeleteQualifier_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
+ if ( (qualNS == 0) || (*qualNS == 0) ) XMP_Throw ( "Empty qualifier namespace URI", kXMPErr_BadSchema );
+ if ( (qualName == 0) || (*qualName == 0) ) XMP_Throw ( "Empty qualifier name", kXMPErr_BadXPath );
+
+ thiz->DeleteQualifier ( schemaNS, propName, qualNS, qualName );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_DoesPropertyExist_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ WXMP_Result * wResult ) /* const */
+{
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_DoesPropertyExist_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
+
+ bool found = thiz.DoesPropertyExist ( schemaNS, propName );
+ wResult->int32Result = found;
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_DoesArrayItemExist_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex,
+ WXMP_Result * wResult ) /* const */
+{
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_DoesArrayItemExist_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
+
+ bool found = thiz.DoesArrayItemExist ( schemaNS, arrayName, itemIndex );
+ wResult->int32Result = found;
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_DoesStructFieldExist_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ WXMP_Result * wResult ) /* const */
+{
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_DoesStructFieldExist_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (structName == 0) || (*structName == 0) ) XMP_Throw ( "Empty struct name", kXMPErr_BadXPath );
+ if ( (fieldNS == 0) || (*fieldNS == 0) ) XMP_Throw ( "Empty field namespace URI", kXMPErr_BadSchema );
+ if ( (fieldName == 0) || (*fieldName == 0) ) XMP_Throw ( "Empty field name", kXMPErr_BadXPath );
+
+ bool found = thiz.DoesStructFieldExist ( schemaNS, structName, fieldNS, fieldName );
+ wResult->int32Result = found;
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_DoesQualifierExist_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName,
+ WXMP_Result * wResult ) /* const */
+{
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_DoesQualifierExist_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
+ if ( (qualNS == 0) || (*qualNS == 0) ) XMP_Throw ( "Empty qualifier namespace URI", kXMPErr_BadSchema );
+ if ( (qualName == 0) || (*qualName == 0) ) XMP_Throw ( "Empty qualifier name", kXMPErr_BadXPath );
+
+ bool found = thiz.DoesQualifierExist ( schemaNS, propName, qualNS, qualName );
+ wResult->int32Result = found;
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_GetLocalizedText_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr genericLang,
+ XMP_StringPtr specificLang,
+ void * actualLang,
+ void * itemValue,
+ XMP_OptionBits * options,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult ) /* const */
+{
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetLocalizedText_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
+ if ( genericLang == 0 ) genericLang = "";
+ if ( (specificLang == 0) ||(*specificLang == 0) ) XMP_Throw ( "Empty specific language", kXMPErr_BadParam );
+
+ XMP_StringPtr langPtr = 0;
+ XMP_StringLen langSize = 0;
+ XMP_StringPtr valuePtr = 0;
+ XMP_StringLen valueSize = 0;
+ if ( options == 0 ) options = &voidOptionBits;
+
+ bool found = thiz.GetLocalizedText ( schemaNS, arrayName, genericLang, specificLang,
+ &langPtr, &langSize, &valuePtr, &valueSize, options );
+ wResult->int32Result = found;
+
+ if ( found ) {
+ if ( actualLang != 0 ) (*SetClientString) ( actualLang, langPtr, langSize );
+ if ( itemValue != 0 ) (*SetClientString) ( itemValue, valuePtr, valueSize );
+ }
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_SetLocalizedText_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr genericLang,
+ XMP_StringPtr specificLang,
+ XMP_StringPtr itemValue,
+ XMP_OptionBits options,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetLocalizedText_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
+ if ( genericLang == 0 ) genericLang = "";
+ if ( (specificLang == 0) ||(*specificLang == 0) ) XMP_Throw ( "Empty specific language", kXMPErr_BadParam );
+ if ( itemValue == 0 ) itemValue = "";
+
+ thiz->SetLocalizedText ( schemaNS, arrayName, genericLang, specificLang, itemValue, options );
+
+ XMP_EXIT
+}
+
+void
+WXMPMeta_DeleteLocalizedText_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr genericLang,
+ XMP_StringPtr specificLang,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_DeleteLocalizedText_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
+ if ( genericLang == 0 ) genericLang = "";
+ if ( (specificLang == 0) ||(*specificLang == 0) ) XMP_Throw ( "Empty specific language", kXMPErr_BadParam );
+
+ thiz->DeleteLocalizedText ( schemaNS, arrayName, genericLang, specificLang );
+
+ XMP_EXIT
+}
+
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_GetProperty_Bool_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_Bool * propValue,
+ XMP_OptionBits * options,
+ WXMP_Result * wResult ) /* const */
+{
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetProperty_Bool_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
+
+ if ( propValue == 0 ) propValue = &voidByte;
+ if ( options == 0 ) options = &voidOptionBits;
+
+ bool value;
+ bool found = thiz.GetProperty_Bool ( schemaNS, propName, &value, options );
+ if ( propValue != 0 ) *propValue = value;
+ wResult->int32Result = found;
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_GetProperty_Int_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_Int32 * propValue,
+ XMP_OptionBits * options,
+ WXMP_Result * wResult ) /* const */
+{
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetProperty_Int_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
+
+ if ( propValue == 0 ) propValue = &voidInt32;
+ if ( options == 0 ) options = &voidOptionBits;
+
+ bool found = thiz.GetProperty_Int ( schemaNS, propName, propValue, options );
+ wResult->int32Result = found;
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_GetProperty_Int64_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_Int64 * propValue,
+ XMP_OptionBits * options,
+ WXMP_Result * wResult ) /* const */
+{
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetProperty_Int64_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
+
+ if ( propValue == 0 ) propValue = &voidInt64;
+ if ( options == 0 ) options = &voidOptionBits;
+
+ bool found = thiz.GetProperty_Int64 ( schemaNS, propName, propValue, options );
+ wResult->int32Result = found;
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_GetProperty_Float_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ double * propValue,
+ XMP_OptionBits * options,
+ WXMP_Result * wResult ) /* const */
+{
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetProperty_Float_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
+
+ if ( propValue == 0 ) propValue = &voidDouble;
+ if ( options == 0 ) options = &voidOptionBits;
+
+ bool found = thiz.GetProperty_Float ( schemaNS, propName, propValue, options );
+ wResult->int32Result = found;
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_GetProperty_Date_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_DateTime * propValue,
+ XMP_OptionBits * options,
+ WXMP_Result * wResult ) /* const */
+{
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetProperty_Date_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
+
+ if ( propValue == 0 ) propValue = &voidDateTime;
+ if ( options == 0 ) options = &voidOptionBits;
+
+ bool found = thiz.GetProperty_Date ( schemaNS, propName, propValue, options );
+ wResult->int32Result = found;
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_SetProperty_Bool_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_Bool propValue,
+ XMP_OptionBits options,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetProperty_Bool_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
+
+ thiz->SetProperty_Bool ( schemaNS, propName, propValue, options );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_SetProperty_Int_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_Int32 propValue,
+ XMP_OptionBits options,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetProperty_Int_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
+
+ thiz->SetProperty_Int ( schemaNS, propName, propValue, options );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_SetProperty_Int64_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_Int64 propValue,
+ XMP_OptionBits options,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetProperty_Int64_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
+
+ thiz->SetProperty_Int64 ( schemaNS, propName, propValue, options );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_SetProperty_Float_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ double propValue,
+ XMP_OptionBits options,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetProperty_Float_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
+
+ thiz->SetProperty_Float ( schemaNS, propName, propValue, options );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_SetProperty_Date_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ const XMP_DateTime & propValue,
+ XMP_OptionBits options,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetProperty_Date_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
+
+ thiz->SetProperty_Date ( schemaNS, propName, propValue, options );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_DumpObject_1 ( XMPMetaRef xmpObjRef,
+ XMP_TextOutputProc outProc,
+ void * refCon,
+ WXMP_Result * wResult ) /* const */
+{
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_DumpObject_1" )
+
+ if ( outProc == 0 ) XMP_Throw ( "Null client output routine", kXMPErr_BadParam );
+
+ thiz.DumpObject ( outProc, refCon );
+ wResult->int32Result = 0;
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_Sort_1 ( XMPMetaRef xmpObjRef,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_Sort_1" )
+
+ thiz->Sort();
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_Erase_1 ( XMPMetaRef xmpObjRef,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_Erase_1" )
+
+ thiz->Erase();
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_Clone_1 ( XMPMetaRef xmpObjRef,
+ XMP_OptionBits options,
+ WXMP_Result * wResult ) /* const */
+{
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_Clone_1" )
+
+ XMPMeta * xClone = new XMPMeta; // ! Don't need an output lock, final ref assignment in client glue.
+ thiz.Clone ( xClone, options );
+ XMP_Assert ( xClone->clientRefs == 0 ); // ! Gets incremented in TXMPMeta::Clone.
+ wResult->ptrResult = xClone;
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_CountArrayItems_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ WXMP_Result * wResult ) /* const */
+{
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_CountArrayItems_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
+
+ XMP_Index count = thiz.CountArrayItems ( schemaNS, arrayName );
+ wResult->int32Result = count;
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_GetObjectName_1 ( XMPMetaRef xmpObjRef,
+ void * objName,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult ) /* const */
+{
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetObjectName_1" )
+
+ XMP_StringPtr namePtr = 0;
+ XMP_StringLen nameSize = 0;
+
+ thiz.GetObjectName ( &namePtr, &nameSize );
+ if ( objName != 0 ) (*SetClientString) ( objName, namePtr, nameSize );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_SetObjectName_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr name,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetObjectName_1" )
+
+ if ( name == 0 ) name = "";
+
+ thiz->SetObjectName ( name );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_GetObjectOptions_1 ( XMPMetaRef xmpObjRef,
+ WXMP_Result * wResult ) /* const */
+{
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetObjectOptions_1" )
+
+ XMP_OptionBits options = thiz.GetObjectOptions();
+ wResult->int32Result = options;
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_SetObjectOptions_1 ( XMPMetaRef xmpObjRef,
+ XMP_OptionBits options,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetObjectOptions_1" )
+
+ thiz->SetObjectOptions ( options );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_ParseFromBuffer_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr buffer,
+ XMP_StringLen bufferSize,
+ XMP_OptionBits options,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_ParseFromBuffer_1" )
+
+ thiz->ParseFromBuffer ( buffer, bufferSize, options );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_SerializeToBuffer_1 ( XMPMetaRef xmpObjRef,
+ void * pktString,
+ XMP_OptionBits options,
+ XMP_StringLen padding,
+ XMP_StringPtr newline,
+ XMP_StringPtr indent,
+ XMP_Index baseIndent,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult ) /* const */
+{
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_SerializeToBuffer_1" )
+
+ XMP_VarString localStr;
+
+ if ( newline == 0 ) newline = "";
+ if ( indent == 0 ) indent = "";
+
+ thiz.SerializeToBuffer ( &localStr, options, padding, newline, indent, baseIndent );
+ if ( pktString != 0 ) (*SetClientString) ( pktString, localStr.c_str(), localStr.size() );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_SetDefaultErrorCallback_1 ( XMPMeta_ErrorCallbackWrapper wrapperProc,
+ XMPMeta_ErrorCallbackProc clientProc,
+ void * context,
+ XMP_Uns32 limit,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPMeta_SetDefaultErrorCallback_1" )
+
+ XMPMeta::SetDefaultErrorCallback ( wrapperProc, clientProc, context, limit );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_SetErrorCallback_1 ( XMPMetaRef xmpObjRef,
+ XMPMeta_ErrorCallbackWrapper wrapperProc,
+ XMPMeta_ErrorCallbackProc clientProc,
+ void * context,
+ XMP_Uns32 limit,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetErrorCallback_1" )
+
+ thiz->SetErrorCallback ( wrapperProc, clientProc, context, limit );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_ResetErrorCallbackLimit_1 ( XMPMetaRef xmpObjRef,
+ XMP_Uns32 limit,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_ResetErrorCallbackLimit_1" )
+
+ thiz->ResetErrorCallbackLimit ( limit );
+
+ XMP_EXIT
+}
+
+// =================================================================================================
+
+#if __cplusplus
+} /* extern "C" */
+#endif
diff --git a/gpr/source/lib/xmp_core/WXMPUtils.cpp b/gpr/source/lib/xmp_core/WXMPUtils.cpp
new file mode 100644
index 0000000..fc7ca17
--- /dev/null
+++ b/gpr/source/lib/xmp_core/WXMPUtils.cpp
@@ -0,0 +1,634 @@
+// =================================================================================================
+// Copyright 2004 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.
+// =================================================================================================
+
+// *** Should change "type * inParam" to "type & inParam"
+
+#include "public/include/XMP_Environment.h" // ! This must be the first include!
+#include "public/include/XMP_Const.h"
+
+#include "public/include/client-glue/WXMPUtils.hpp"
+
+#include "XMPCore_Impl.hpp"
+#include "XMPUtils.hpp"
+
+#if XMP_WinBuild
+ #pragma warning ( disable : 4101 ) // unreferenced local variable
+ #pragma warning ( disable : 4189 ) // local variable is initialized but not referenced
+ #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning)
+ #if XMP_DebugBuild
+ #pragma warning ( disable : 4297 ) // function assumed not to throw an exception but does
+ #endif
+#endif
+
+#if __cplusplus
+extern "C" {
+#endif
+
+// =================================================================================================
+// Class Static Wrappers
+// =====================
+
+void
+WXMPUtils_ComposeArrayItemPath_1 ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex,
+ void * itemPath,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_ComposeArrayItemPath_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
+
+ XMP_VarString localStr;
+
+ XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &localStr );
+ if ( itemPath != 0 ) (*SetClientString) ( itemPath, localStr.c_str(), localStr.size() );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPUtils_ComposeStructFieldPath_1 ( XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ void * fieldPath,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_ComposeStructFieldPath_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (structName == 0) || (*structName == 0) ) XMP_Throw ( "Empty struct name", kXMPErr_BadXPath );
+ if ( (fieldNS == 0) || (*fieldNS == 0) ) XMP_Throw ( "Empty field namespace URI", kXMPErr_BadSchema );
+ if ( (fieldName == 0) || (*fieldName == 0) ) XMP_Throw ( "Empty field name", kXMPErr_BadXPath );
+
+ XMP_VarString localStr;
+
+ XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &localStr );
+ if ( fieldPath != 0 ) (*SetClientString) ( fieldPath, localStr.c_str(), localStr.size() );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPUtils_ComposeQualifierPath_1 ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName,
+ void * qualPath,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_ComposeQualifierPath_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
+ if ( (qualNS == 0) || (*qualNS == 0) ) XMP_Throw ( "Empty qualifier namespace URI", kXMPErr_BadSchema );
+ if ( (qualName == 0) || (*qualName == 0) ) XMP_Throw ( "Empty qualifier name", kXMPErr_BadXPath );
+
+ XMP_VarString localStr;
+
+ XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &localStr );
+ if ( qualPath != 0 ) (*SetClientString) ( qualPath, localStr.c_str(), localStr.size() );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPUtils_ComposeLangSelector_1 ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr langName,
+ void * selPath,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_ComposeLangSelector_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
+ if ( (langName == 0) || (*langName == 0) ) XMP_Throw ( "Empty language name", kXMPErr_BadParam );
+
+ XMP_VarString localStr;
+
+ XMPUtils::ComposeLangSelector ( schemaNS, arrayName, langName, &localStr );
+ if ( selPath != 0 ) (*SetClientString) ( selPath, localStr.c_str(), localStr.size() );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPUtils_ComposeFieldSelector_1 ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ XMP_StringPtr fieldValue,
+ void * selPath,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_ComposeFieldSelector_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
+ if ( (fieldNS == 0) || (*fieldNS == 0) ) XMP_Throw ( "Empty field namespace URI", kXMPErr_BadSchema );
+ if ( (fieldName == 0) || (*fieldName == 0) ) XMP_Throw ( "Empty field name", kXMPErr_BadXPath );
+ if ( fieldValue == 0 ) fieldValue = "";
+
+ XMP_VarString localStr;
+
+ XMPUtils::ComposeFieldSelector ( schemaNS, arrayName, fieldNS, fieldName, fieldValue, &localStr );
+ if ( selPath != 0 ) (*SetClientString) ( selPath, localStr.c_str(), localStr.size() );
+
+ XMP_EXIT
+}
+
+// =================================================================================================
+
+void
+WXMPUtils_ConvertFromBool_1 ( XMP_Bool binValue,
+ void * strValue,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_ConvertFromBool_1" )
+
+ XMP_VarString localStr;
+
+ XMPUtils::ConvertFromBool ( binValue, &localStr );
+ if ( strValue != 0 ) (*SetClientString) ( strValue, localStr.c_str(), localStr.size() );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPUtils_ConvertFromInt_1 ( XMP_Int32 binValue,
+ XMP_StringPtr format,
+ void * strValue,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_ConvertFromInt_1" )
+
+ if ( format == 0 ) format = "";
+
+ XMP_VarString localStr;
+
+ XMPUtils::ConvertFromInt ( binValue, format, &localStr );
+ if ( strValue != 0 ) (*SetClientString) ( strValue, localStr.c_str(), localStr.size() );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPUtils_ConvertFromInt64_1 ( XMP_Int64 binValue,
+ XMP_StringPtr format,
+ void * strValue,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_ConvertFromInt64_1" )
+
+ if ( format == 0 ) format = "";
+
+ XMP_VarString localStr;
+
+ XMPUtils::ConvertFromInt64 ( binValue, format, &localStr );
+ if ( strValue != 0 ) (*SetClientString) ( strValue, localStr.c_str(), localStr.size() );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPUtils_ConvertFromFloat_1 ( double binValue,
+ XMP_StringPtr format,
+ void * strValue,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_ConvertFromFloat_1" )
+
+ if ( format == 0 ) format = "";
+
+ XMP_VarString localStr;
+
+ XMPUtils::ConvertFromFloat ( binValue, format, &localStr );
+ if ( strValue != 0 ) (*SetClientString) ( strValue, localStr.c_str(), localStr.size() );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPUtils_ConvertFromDate_1 ( const XMP_DateTime & binValue,
+ void * strValue,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_ConvertFromDate_1" )
+
+ XMP_VarString localStr;
+
+ XMPUtils::ConvertFromDate( binValue, &localStr );
+ if ( strValue != 0 ) (*SetClientString) ( strValue, localStr.c_str(), localStr.size() );
+
+ XMP_EXIT
+}
+
+// =================================================================================================
+
+void
+WXMPUtils_ConvertToBool_1 ( XMP_StringPtr strValue,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_ConvertToBool_1" )
+
+ if ( (strValue == 0) || (*strValue == 0) ) XMP_Throw ( "Empty string value", kXMPErr_BadParam);
+ XMP_Bool result = XMPUtils::ConvertToBool ( strValue );
+ wResult->int32Result = result;
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPUtils_ConvertToInt_1 ( XMP_StringPtr strValue,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_ConvertToInt_1" )
+
+ if ( (strValue == 0) || (*strValue == 0) ) XMP_Throw ( "Empty string value", kXMPErr_BadParam);
+ XMP_Int32 result = XMPUtils::ConvertToInt ( strValue );
+ wResult->int32Result = result;
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPUtils_ConvertToInt64_1 ( XMP_StringPtr strValue,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_ConvertToInt64_1" )
+
+ if ( (strValue == 0) || (*strValue == 0) ) XMP_Throw ( "Empty string value", kXMPErr_BadParam);
+ XMP_Int64 result = XMPUtils::ConvertToInt64 ( strValue );
+ wResult->int64Result = result;
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPUtils_ConvertToFloat_1 ( XMP_StringPtr strValue,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_ConvertToFloat_1")
+
+ if ( (strValue == 0) || (*strValue == 0) ) XMP_Throw ( "Empty string value", kXMPErr_BadParam);
+ double result = XMPUtils::ConvertToFloat ( strValue );
+ wResult->floatResult = result;
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPUtils_ConvertToDate_1 ( XMP_StringPtr strValue,
+ XMP_DateTime * binValue,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_ConvertToDate_1" )
+
+ if ( binValue == 0 ) XMP_Throw ( "Null output date", kXMPErr_BadParam); // ! Pointer is from the client.
+ XMPUtils::ConvertToDate ( strValue, binValue );
+
+ XMP_EXIT
+}
+
+// =================================================================================================
+
+void
+WXMPUtils_CurrentDateTime_1 ( XMP_DateTime * time,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_CurrentDateTime_1" )
+
+ if ( time == 0 ) XMP_Throw ( "Null output date", kXMPErr_BadParam);
+ XMPUtils::CurrentDateTime ( time );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPUtils_SetTimeZone_1 ( XMP_DateTime * time,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_SetTimeZone_1" )
+
+ if ( time == 0 ) XMP_Throw ( "Null output date", kXMPErr_BadParam);
+ XMPUtils::SetTimeZone ( time );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPUtils_ConvertToUTCTime_1 ( XMP_DateTime * time,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_ConvertToUTCTime_1" )
+
+ if ( time == 0 ) XMP_Throw ( "Null output date", kXMPErr_BadParam);
+ XMPUtils::ConvertToUTCTime ( time );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPUtils_ConvertToLocalTime_1 ( XMP_DateTime * time,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_ConvertToLocalTime_1" )
+
+ if ( time == 0 ) XMP_Throw ( "Null output date", kXMPErr_BadParam);
+ XMPUtils::ConvertToLocalTime ( time );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPUtils_CompareDateTime_1 ( const XMP_DateTime & left,
+ const XMP_DateTime & right,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_CompareDateTime_1" )
+
+ int result = XMPUtils::CompareDateTime ( left, right );
+ wResult->int32Result = result;
+
+ XMP_EXIT
+}
+
+// =================================================================================================
+
+void
+WXMPUtils_EncodeToBase64_1 ( XMP_StringPtr rawStr,
+ XMP_StringLen rawLen,
+ void * encodedStr,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_EncodeToBase64_1" )
+
+ XMP_VarString localStr;
+
+ XMPUtils::EncodeToBase64 ( rawStr, rawLen, &localStr );
+ if ( encodedStr != 0 ) (*SetClientString) ( encodedStr, localStr.c_str(), localStr.size() );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPUtils_DecodeFromBase64_1 ( XMP_StringPtr encodedStr,
+ XMP_StringLen encodedLen,
+ void * rawStr,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_DecodeFromBase64_1" )
+
+ XMP_VarString localStr;
+
+ XMPUtils::DecodeFromBase64 ( encodedStr, encodedLen, &localStr );
+ if ( rawStr != 0 ) (*SetClientString) ( rawStr, localStr.c_str(), localStr.size() );
+
+ XMP_EXIT
+}
+
+// =================================================================================================
+
+void
+WXMPUtils_PackageForJPEG_1 ( XMPMetaRef wxmpObj,
+ void * stdStr,
+ void * extStr,
+ void * digestStr,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_PackageForJPEG_1" )
+
+ XMP_VarString localStdStr;
+ XMP_VarString localExtStr;
+ XMP_VarString localDigestStr;
+
+ const XMPMeta & xmpObj = WtoXMPMeta_Ref ( wxmpObj );
+ XMP_AutoLock metaLock ( &xmpObj.lock, kXMP_ReadLock );
+
+ XMPUtils::PackageForJPEG ( xmpObj, &localStdStr, &localExtStr, &localDigestStr );
+ if ( stdStr != 0 ) (*SetClientString) ( stdStr, localStdStr.c_str(), localStdStr.size() );
+ if ( extStr != 0 ) (*SetClientString) ( extStr, localExtStr.c_str(), localExtStr.size() );
+ if ( digestStr != 0 ) (*SetClientString) ( digestStr, localDigestStr.c_str(), localDigestStr.size() );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPUtils_MergeFromJPEG_1 ( XMPMetaRef wfullXMP,
+ XMPMetaRef wextendedXMP,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_MergeFromJPEG_1" )
+
+ if ( wfullXMP == 0 ) XMP_Throw ( "Output XMP pointer is null", kXMPErr_BadParam );
+ if ( wfullXMP == wextendedXMP ) XMP_Throw ( "Full and extended XMP pointers match", kXMPErr_BadParam );
+
+ XMPMeta * fullXMP = WtoXMPMeta_Ptr ( wfullXMP );
+ XMP_AutoLock fullXMPLock ( &fullXMP->lock, kXMP_WriteLock );
+
+ const XMPMeta & extendedXMP = WtoXMPMeta_Ref ( wextendedXMP );
+ XMP_AutoLock extendedXMPLock ( &extendedXMP.lock, kXMP_ReadLock );
+
+ XMPUtils::MergeFromJPEG ( fullXMP, extendedXMP );
+
+ XMP_EXIT
+}
+
+// =================================================================================================
+
+void
+WXMPUtils_CatenateArrayItems_1 ( XMPMetaRef wxmpObj,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr separator,
+ XMP_StringPtr quotes,
+ XMP_OptionBits options,
+ void * catedStr,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_CatenateArrayItems_1" )
+
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
+
+ if ( separator == 0 ) separator = "; ";
+ if ( quotes == 0 ) quotes = "\"";
+
+ XMP_VarString localStr;
+
+ const XMPMeta & xmpObj = WtoXMPMeta_Ref ( wxmpObj );
+ XMP_AutoLock metaLock ( &xmpObj.lock, kXMP_ReadLock );
+
+ XMPUtils::CatenateArrayItems ( xmpObj, schemaNS, arrayName, separator, quotes, options, &localStr );
+ if ( catedStr != 0 ) (*SetClientString) ( catedStr, localStr.c_str(), localStr.size() );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPUtils_SeparateArrayItems_1 ( XMPMetaRef wxmpObj,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_OptionBits options,
+ XMP_StringPtr catedStr,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_SeparateArrayItems_1" )
+
+ if ( wxmpObj == 0 ) XMP_Throw ( "Output XMP pointer is null", kXMPErr_BadParam );
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
+ if ( catedStr == 0 ) catedStr = "";
+
+ XMPMeta * xmpObj = WtoXMPMeta_Ptr ( wxmpObj );
+ XMP_AutoLock metaLock ( &xmpObj->lock, kXMP_WriteLock );
+
+ XMPUtils::SeparateArrayItems ( xmpObj, schemaNS, arrayName, options, catedStr );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPUtils_ApplyTemplate_1 ( XMPMetaRef wWorkingXMP,
+ XMPMetaRef wTemplateXMP,
+ XMP_OptionBits actions,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_ApplyTemplate_1" )
+
+ XMP_Assert ( (wWorkingXMP != 0) && (wTemplateXMP != 0) ); // Client glue enforced.
+
+ XMPMeta * workingXMP = WtoXMPMeta_Ptr ( wWorkingXMP );
+ XMP_AutoLock workingLock ( &workingXMP->lock, kXMP_WriteLock );
+
+ const XMPMeta & templateXMP = WtoXMPMeta_Ref ( wTemplateXMP );
+ XMP_AutoLock templateLock ( &templateXMP.lock, kXMP_ReadLock );
+
+ XMPUtils::ApplyTemplate ( workingXMP, templateXMP, actions );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPUtils_RemoveProperties_1 ( XMPMetaRef wxmpObj,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_OptionBits options,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_RemoveProperties_1" )
+
+ if ( wxmpObj == 0 ) XMP_Throw ( "Output XMP pointer is null", kXMPErr_BadParam );
+ if ( schemaNS == 0 ) schemaNS = "";
+ if ( propName == 0 ) propName = "";
+
+ XMPMeta * xmpObj = WtoXMPMeta_Ptr ( wxmpObj );
+ XMP_AutoLock metaLock ( &xmpObj->lock, kXMP_WriteLock );
+
+ XMPUtils::RemoveProperties ( xmpObj, schemaNS, propName, options );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPUtils_DuplicateSubtree_1 ( XMPMetaRef wSource,
+ XMPMetaRef wDest,
+ XMP_StringPtr sourceNS,
+ XMP_StringPtr sourceRoot,
+ XMP_StringPtr destNS,
+ XMP_StringPtr destRoot,
+ XMP_OptionBits options,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_DuplicateSubtree_1" )
+
+ if ( wDest == 0 ) XMP_Throw ( "Output XMP pointer is null", kXMPErr_BadParam );
+ if ( (sourceNS == 0) || (*sourceNS == 0) ) XMP_Throw ( "Empty source schema URI", kXMPErr_BadSchema );
+ if ( (sourceRoot == 0) || (*sourceRoot == 0) ) XMP_Throw ( "Empty source root name", kXMPErr_BadXPath );
+ if ( destNS == 0 ) destNS = sourceNS;
+ if ( destRoot == 0 ) destRoot = sourceRoot;
+
+ const XMPMeta & source = WtoXMPMeta_Ref ( wSource );
+ XMP_AutoLock sourceLock ( &source.lock, kXMP_ReadLock, (wSource != wDest) );
+
+ XMPMeta * dest = WtoXMPMeta_Ptr ( wDest );
+ XMP_AutoLock destLock ( &dest->lock, kXMP_WriteLock );
+
+ XMPUtils::DuplicateSubtree ( source, dest, sourceNS, sourceRoot, destNS, destRoot, options );
+
+ XMP_EXIT
+}
+
+// =================================================================================================
+
+#if __cplusplus
+} /* extern "C" */
+#endif
diff --git a/gpr/source/lib/xmp_core/XMLParserAdapter.hpp b/gpr/source/lib/xmp_core/XMLParserAdapter.hpp
new file mode 100644
index 0000000..ff9b877
--- /dev/null
+++ b/gpr/source/lib/xmp_core/XMLParserAdapter.hpp
@@ -0,0 +1,155 @@
+#ifndef __XMLParserAdapter_hpp__
+#define __XMLParserAdapter_hpp__
+
+// =================================================================================================
+// Copyright 2005 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.
+// =================================================================================================
+
+#include "public/include/XMP_Environment.h" // ! Must be the first #include!
+#include "public/include/XMP_Const.h"
+
+#include "XMP_LibUtils.hpp"
+
+#include <string>
+#include <vector>
+
+// =================================================================================================
+// XML_Node details
+//
+// The XML_Nodes are used only during the XML/RDF parsing process. This presently uses an XML parser
+// to create an XML tree, then a recursive descent RDF recognizer to build the corresponding XMP.
+// This makes it easier to swap XML parsers and provides a clean separation of XML and RDF issues.
+// The overall parsing would be faster and use less memory if the RDF recognition were done on the
+// fly using a state machine. But it was much easier to write the recursive descent version. The
+// current implementation is pretty fast in absolute terms, so being faster might not be crucial.
+//
+// Like the XMP tree, the XML tree contains vectors of pointers for down links, and offspring have
+// a pointer to their parent. Unlike the XMP tree, this is an exact XML document tree. There are no
+// introduced top level namespace nodes or rearrangement of the nodes..
+//
+// The exact state of namespaces can vary during the XML parsing, depending on the parser in use.
+// By the time the RDF recognition is done though, the namespaces must be normalized. All of the
+// used namespaces must be registered, this is done automatically if necessary. All of the "live"
+// namespace prefixes will be unique. The ns field of an XML_Node is the namespace URI, the name
+// field contains a qualified name (prefix:local). This includes default namespace mapping, the
+// URI and prefix will be missing only for elements and attributes in no namespace.
+
+class XML_Node;
+
+typedef XML_Node * XML_NodePtr; // Handy for things like: XML_Node * a, b; - b is XML_Node, not XML_Node*!
+
+enum { kRootNode = 0, kElemNode = 1, kAttrNode = 2, kCDataNode = 3, kPINode = 4 };
+
+#define IsWhitespaceChar(ch) ( ((ch) == ' ') || ((ch) == 0x09) || ((ch) == 0x0A) || ((ch) == 0x0D) )
+
+typedef std::vector<XML_NodePtr> XML_NodeVector;
+typedef XML_NodeVector::iterator XML_NodePos;
+typedef XML_NodeVector::const_iterator XML_cNodePos;
+
+#if 0 // Pattern for iterating over the children or attributes:
+ for ( size_t xxNum = 0, xxLim = _node_->_offspring_.size(); xxNum < xxLim; ++xxNum ) {
+ const XML_NodePtr _curr_ = _node_->_offspring_[xxNum];
+ }
+#endif
+
+class XML_Node {
+public:
+
+ // Intended for lightweight internal use. Clients are expected to use the data directly.
+
+ XMP_Uns8 kind;
+ std::string ns, name, value;
+ size_t nsPrefixLen;
+ XML_NodePtr parent;
+ XML_NodeVector attrs;
+ XML_NodeVector content;
+
+ bool IsWhitespaceNode() const;
+ bool IsLeafContentNode() const; // An empty element or one with a single character data child node.
+ bool IsEmptyLeafNode() const;
+
+ XMP_StringPtr GetAttrValue ( XMP_StringPtr attrName ) const;
+ void SetAttrValue ( XMP_StringPtr attrName, XMP_StringPtr attrValue );
+
+ XMP_StringPtr GetLeafContentValue() const;
+ std::string* GetLeafContentPtr() const;
+ void SetLeafContentValue ( XMP_StringPtr value );
+
+ size_t CountNamedElements ( XMP_StringPtr nsURI, XMP_StringPtr localName ) const; // Number of child elements with this name.
+ XML_NodePtr GetNamedElement ( XMP_StringPtr nsURI, XMP_StringPtr localName, size_t which = 0 );
+
+ void Dump ( std::string * buffer );
+ void Serialize ( std::string * buffer );
+
+ void RemoveAttrs();
+ void RemoveContent();
+ void ClearNode();
+
+ XML_Node ( XML_NodePtr _parent, XMP_StringPtr _name, XMP_Uns8 _kind )
+ : kind(_kind), name(_name), parent(_parent), nsPrefixLen(0) {};
+
+ XML_Node ( XML_NodePtr _parent, const std::string & _name, XMP_Uns8 _kind )
+ : kind(_kind), name(_name), parent(_parent), nsPrefixLen(0) {};
+
+ virtual ~XML_Node() { RemoveAttrs(); RemoveContent(); };
+
+private:
+
+ XML_Node() : kind(0), parent(0) {}; // ! Hidden to make sure parent pointer is always set.
+
+};
+
+// =================================================================================================
+// Abstract base class for XML parser adapters used by the XMP toolkit.
+
+enum { kXMLPendingInputMax = 16 };
+
+class XMLParserAdapter {
+public:
+
+ XMLParserAdapter() : tree(0,"",kRootNode), rootNode(0), rootCount(0),
+ charEncoding(XMP_OptionBits(-1)), pendingCount(0),
+ errorCallback(0)
+ {
+ #if XMP_DebugBuild
+ parseLog = 0;
+ #endif
+ };
+
+ virtual ~XMLParserAdapter() {};
+
+ virtual void ParseBuffer ( const void * buffer, size_t length, bool last ) = 0;
+
+ virtual void SetErrorCallback ( GenericErrorCallback * ec )
+ { this->errorCallback = ec; };
+
+ virtual void NotifyClient ( XMP_ErrorSeverity severity, XMP_Error & error )
+ {
+ if (this->errorCallback)
+ this->errorCallback->NotifyClient( severity, error );
+ }
+
+ XML_Node tree;
+ XML_NodeVector parseStack;
+ XML_NodePtr rootNode;
+ size_t rootCount;
+
+ XMP_OptionBits charEncoding;
+ size_t pendingCount;
+ unsigned char pendingInput[kXMLPendingInputMax]; // Buffered input for character encoding checks.
+
+ GenericErrorCallback * errorCallback; // Set if the relevant XMPCore or XMPFiles object has one.
+
+ #if XMP_DebugBuild
+ FILE * parseLog;
+ #endif
+
+};
+
+// =================================================================================================
+
+#endif // __XMLParserAdapter_hpp__
diff --git a/gpr/source/lib/xmp_core/XML_Node.cpp b/gpr/source/lib/xmp_core/XML_Node.cpp
new file mode 100644
index 0000000..c7ac1ce
--- /dev/null
+++ b/gpr/source/lib/xmp_core/XML_Node.cpp
@@ -0,0 +1,473 @@
+// =================================================================================================
+// Copyright 2007 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.
+// =================================================================================================
+
+#include "public/include/XMP_Environment.h" // ! Must be the first #include!
+#include "XMLParserAdapter.hpp"
+
+#include <map>
+#include <cstring>
+#include <cstdio>
+
+// ! Can't include XMP..._Impl.hpp - used by both Core and Files.
+#define XMP_LitNMatch(s,l,n) (std::strncmp((s),(l),(n)) == 0)
+
+#if XMP_WinBuild
+ #define snprintf _snprintf
+ #pragma warning ( disable : 4996 ) // snprintf is safe
+#endif
+
+// =================================================================================================
+
+#if 0 // Pattern for iterating over the children or attributes:
+ for ( size_t xxNum = 0, xxLim = _node_->_offspring_->size(); xxNum < xxLim; ++xxNum ) {
+ const XML_NodePtr _curr_ = _node_->_offspring_[xxNum];
+ }
+#endif
+
+// =================================================================================================
+// XML_Node::IsWhitespaceNode
+//===========================
+
+bool XML_Node::IsWhitespaceNode() const
+{
+ if ( this->kind != kCDataNode ) return false;
+
+ for ( size_t i = 0; i < this->value.size(); ++i ) {
+ unsigned char ch = this->value[i];
+ if ( IsWhitespaceChar ( ch ) ) continue;
+ // *** Add checks for other whitespace characters.
+ return false; // All the checks failed, this isn't whitespace.
+ }
+
+ return true;
+
+} // XML_Node::IsWhitespaceNode
+
+// =================================================================================================
+// XML_Node::IsLeafContentNode
+//============================
+
+bool XML_Node::IsLeafContentNode() const
+{
+ if ( this->kind != kElemNode ) return false;
+ if ( this->content.size() == 0 ) return true;
+ if ( this->content.size() > 1 ) return false;
+ if ( this->content[0]->kind != kCDataNode ) return false;
+
+ return true;
+
+} // XML_Node::IsLeafContentNode
+
+// =================================================================================================
+// XML_Node::IsEmptyLeafNode
+//==========================
+
+bool XML_Node::IsEmptyLeafNode() const
+{
+
+ if ( (this->kind != kElemNode) || (this->content.size() != 0) ) return false;
+ return true;
+
+} // XML_Node::IsEmptyLeafNode
+
+// =================================================================================================
+// XML_Node::GetAttrValue
+//=======================
+
+XMP_StringPtr XML_Node::GetAttrValue ( XMP_StringPtr attrName ) const
+{
+
+ for ( size_t i = 0, aLim = this->attrs.size(); i < aLim; ++i ) {
+ XML_Node * attrPtr = this->attrs[i];
+ if ( ! attrPtr->ns.empty() ) continue; // This form of GetAttrValue is for attrs in no namespace.
+ if ( attrPtr->name == attrName ) return attrPtr->value.c_str();
+ }
+
+ return 0; // Not found.
+
+} // XML_Node::GetAttrValue
+
+// =================================================================================================
+// XML_Node::SetAttrValue
+//=======================
+
+void XML_Node::SetAttrValue ( XMP_StringPtr attrName, XMP_StringPtr attrValue )
+{
+
+ for ( size_t i = 0, aLim = this->attrs.size(); i < aLim; ++i ) {
+ XML_Node * attrPtr = this->attrs[i];
+ if ( ! attrPtr->ns.empty() ) continue; // This form of SetAttrValue is for attrs in no namespace.
+ if ( attrPtr->name == attrName ) {
+ attrPtr->value = attrValue;
+ return;
+ }
+ }
+
+} // XML_Node::SetAttrValue
+
+// =================================================================================================
+// XML_Node::GetLeafContentValue
+//==============================
+
+XMP_StringPtr XML_Node::GetLeafContentValue() const
+{
+ if ( (! this->IsLeafContentNode()) || this->content.empty() ) return "";
+
+ return this->content[0]->value.c_str();
+
+} // XML_Node::GetLeafContentValue
+
+// =================================================================================================
+// XML_Node::GetLeafContentValue
+//==============================
+
+std::string* XML_Node::GetLeafContentPtr() const
+{
+ if ( (! this->IsLeafContentNode()) || this->content.empty() ) return 0;
+
+ return &this->content[0]->value;
+
+} // XML_Node::GetLeafContentValue
+
+// =================================================================================================
+// XML_Node::SetLeafContentValue
+//==============================
+
+void XML_Node::SetLeafContentValue ( XMP_StringPtr newValue )
+{
+ XML_Node * valueNode;
+
+ if ( ! this->content.empty() ) {
+ valueNode = this->content[0];
+ } else {
+ valueNode = new XML_Node ( this, "", kCDataNode );
+ this->content.push_back ( valueNode );
+ }
+
+ valueNode->value = newValue;
+
+} // XML_Node::SetLeafContentValue
+
+// =================================================================================================
+// XML_Node::CountNamedElements
+//=============================
+
+size_t XML_Node::CountNamedElements ( XMP_StringPtr nsURI, XMP_StringPtr localName ) const
+{
+ size_t count = 0;
+
+ for ( size_t i = 0, vLim = this->content.size(); i < vLim; ++i ) {
+ const XML_Node & child = *this->content[i];
+ if ( child.ns != nsURI ) continue;
+ if ( strcmp ( localName, child.name.c_str()+child.nsPrefixLen ) != 0 ) continue;
+ ++count;
+ }
+
+ return count;
+
+} // XML_Node::CountNamedElements
+
+// =================================================================================================
+// XML_Node::GetNamedElement
+//==========================
+
+XML_NodePtr XML_Node::GetNamedElement ( XMP_StringPtr nsURI, XMP_StringPtr localName, size_t which /* = 0 */ )
+{
+
+ for ( size_t i = 0, vLim = this->content.size(); i < vLim; ++i ) {
+ XML_Node * childPtr = this->content[i];
+ if ( childPtr->ns != nsURI ) continue;
+ if ( strcmp ( localName, childPtr->name.c_str()+childPtr->nsPrefixLen ) != 0 ) continue;
+ if ( which == 0 ) return childPtr;
+ --which;
+ }
+
+ return 0; /// Not found.
+
+} // XML_Node::GetNamedElement
+
+// =================================================================================================
+// DumpNodeList
+// ============
+
+static const char * kNodeKinds[] = { "root", "elem", "attr", "cdata", "pi" };
+
+static void DumpNodeList ( std::string * buffer, const XML_NodeVector & list, int indent )
+{
+
+ for ( size_t i = 0, limit = list.size(); i < limit; ++i ) {
+
+ const XML_Node * node = list[i];
+
+ for ( int t = indent; t > 0; --t ) *buffer += " ";
+ if ( node->IsWhitespaceNode() ) {
+ *buffer += "-- whitespace --\n";
+ continue;
+ }
+
+ *buffer += node->name;
+ *buffer += " - ";
+ *buffer += kNodeKinds[node->kind];
+ if ( ! node->value.empty() ) {
+ *buffer += ", value=\"";
+ *buffer += node->value;
+ *buffer += "\"";
+ }
+ if ( ! node->ns.empty() ) {
+ *buffer += ", ns=\"";
+ *buffer += node->ns;
+ *buffer += "\"";
+ }
+ if ( node->nsPrefixLen != 0 ) {
+ *buffer += ", prefixLen=";
+ char numBuf [20];
+ snprintf ( numBuf, sizeof(numBuf), "%d", (int)node->nsPrefixLen );
+ *buffer += numBuf;
+ }
+ *buffer += "\n";
+
+ if ( ! node->attrs.empty() ) {
+ for ( int t = indent+1; t > 0; --t ) *buffer += " ";
+ *buffer += "attrs:\n";
+ DumpNodeList ( buffer, node->attrs, indent+2 );
+ }
+
+ if ( ! node->content.empty() ) {
+ DumpNodeList ( buffer, node->content, indent+1 );
+ }
+
+ }
+
+} // DumpNodeList
+
+// =================================================================================================
+// XML_Node::Dump
+//===============
+
+void XML_Node::Dump ( std::string * buffer )
+{
+
+ *buffer = "Dump of XML_Node tree\n";
+
+ *buffer += "Root info: name=\"";
+ *buffer += this->name;
+ *buffer += "\", value=\"";
+ *buffer += this->value;
+ *buffer += "\", ns=\"";
+ *buffer += this->ns;
+ *buffer += "\", kind=";
+ *buffer += kNodeKinds[this->kind];
+ *buffer += "\n";
+
+ if ( ! this->attrs.empty() ) {
+ *buffer += " attrs:\n";
+ DumpNodeList ( buffer, this->attrs, 2 );
+ }
+ *buffer += "\n";
+
+ DumpNodeList ( buffer, this->content, 0 );
+
+} // XML_Node::Dump
+
+// =================================================================================================
+// SerializeOneNode
+// ================
+
+static void SerializeOneNode ( std::string * buffer, const XML_Node & node )
+{
+ size_t i, limit;
+ XMP_StringPtr namePtr = node.name.c_str();
+ if ( XMP_LitNMatch ( namePtr, "_dflt_:", 7 ) ) namePtr += 7; // Hack for default namespaces.
+
+ switch ( node.kind ) {
+
+ case kElemNode:
+ *buffer += '<';
+ *buffer += namePtr;
+ for ( i = 0, limit = node.attrs.size(); i < limit; ++i ) {
+ SerializeOneNode ( buffer, *node.attrs[i] );
+ }
+ if ( node.content.empty() ) {
+ *buffer += "/>";
+ } else {
+ *buffer += '>';
+ for ( i = 0, limit = node.content.size(); i < limit; ++i ) {
+ SerializeOneNode ( buffer, *node.content[i] );
+ }
+ *buffer += "</";
+ *buffer += namePtr;
+ *buffer += '>';
+ }
+ break;
+
+ case kAttrNode:
+ *buffer += ' ';
+ *buffer += namePtr;
+ *buffer += "=\"";
+ *buffer += node.value;
+ *buffer += '"';
+ break;
+
+ case kCDataNode:
+ *buffer += node.value;
+ break;
+
+ case kPINode:
+ *buffer += node.value; // *** Note that we're dropping PIs during the Expat parse.
+ break;
+
+ }
+
+} // SerializeOneNode
+
+// =================================================================================================
+// CollectNamespaceDecls
+// =====================
+
+typedef std::map < std::string, std::string > NamespaceMap;
+
+static void CollectNamespaceDecls ( NamespaceMap * nsMap, const XML_Node & node )
+{
+ size_t i, limit;
+
+ if ( ! node.ns.empty() ) {
+ size_t nameMid = 0;
+ while ( node.name[nameMid] != ':' ) ++nameMid;
+ std::string prefix = node.name.substr ( 0, nameMid );
+ (*nsMap)[prefix] = node.ns;
+ }
+
+ if ( node.kind == kElemNode ) {
+
+ for ( i = 0, limit = node.attrs.size(); i < limit; ++i ) {
+ CollectNamespaceDecls ( nsMap, *node.attrs[i] );
+ }
+
+ for ( i = 0, limit = node.content.size(); i < limit; ++i ) {
+ const XML_Node & content = *node.content[i];
+ if ( content.kind == kElemNode ) CollectNamespaceDecls ( nsMap, content );
+ }
+
+ }
+
+} // CollectNamespaceDecls
+
+// =================================================================================================
+// XML_Node::Serialize
+//====================
+
+void XML_Node::Serialize ( std::string * buffer )
+{
+ buffer->erase();
+
+ if ( this->kind != kRootNode ) {
+
+ SerializeOneNode ( buffer, *this );
+
+ } else {
+
+ // Do the outermost level here, in order to add the XML version and namespace declarations.
+
+ *buffer += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+
+ for ( size_t outer = 0, oLimit = this->content.size(); outer < oLimit; ++outer ) {
+
+ const XML_Node & node = *this->content[outer];
+
+ if ( node.kind != kElemNode ) {
+
+ SerializeOneNode ( buffer, node );
+
+ } else {
+
+ XMP_StringPtr namePtr = node.name.c_str();
+ if ( XMP_LitNMatch ( namePtr, "_dflt_:", 7 ) ) namePtr += 7; // Hack for default namespaces.
+
+ *buffer += '<';
+ *buffer += namePtr;
+
+ NamespaceMap nsMap;
+ CollectNamespaceDecls ( &nsMap, node );
+ NamespaceMap::iterator nsDecl = nsMap.begin();
+ NamespaceMap::iterator nsEnd = nsMap.end();
+ for ( ; nsDecl != nsEnd; ++nsDecl ) {
+ const std::string & prefix = nsDecl->first;
+ *buffer += " xmlns";
+ if ( prefix != "_dflt_" ) { *buffer += ':'; *buffer += prefix; }
+ *buffer += "=\"";
+ *buffer += nsDecl->second;
+ *buffer += '"';
+ }
+
+ for ( size_t attr = 0, aLimit = node.attrs.size(); attr < aLimit; ++attr ) {
+ SerializeOneNode ( buffer, *node.attrs[attr] );
+ }
+
+ if ( node.content.empty() ) {
+ *buffer += "/>";
+ } else {
+ *buffer += '>';
+ for ( size_t child = 0, cLimit = node.content.size(); child < cLimit; ++child ) {
+ SerializeOneNode ( buffer, *node.content[child] );
+ }
+ *buffer += "</";
+ *buffer += namePtr;
+ *buffer += '>';
+ }
+
+ }
+
+ }
+
+ }
+
+
+} // XML_Node::Serialize
+
+// =================================================================================================
+// XML_Node::RemoveAttrs
+//======================
+
+void XML_Node::RemoveAttrs()
+{
+
+ for ( size_t i = 0, vLim = this->attrs.size(); i < vLim; ++i ) delete this->attrs[i];
+ this->attrs.clear();
+
+} // XML_Node::RemoveAttrs
+
+// =================================================================================================
+// XML_Node::RemoveContent
+//========================
+
+void XML_Node::RemoveContent()
+{
+
+ for ( size_t i = 0, vLim = this->content.size(); i < vLim; ++i ) delete this->content[i];
+ this->content.clear();
+
+} // XML_Node::RemoveContent
+
+// =================================================================================================
+// XML_Node::ClearNode
+//====================
+
+void XML_Node::ClearNode()
+{
+
+ this->kind = 0;
+ this->ns.erase();
+ this->name.erase();
+ this->value.erase();
+
+ this->RemoveAttrs();
+ this->RemoveContent();
+
+} // XML_Node::ClearNode
+
+// =================================================================================================
diff --git a/gpr/source/lib/xmp_core/XMPCore_Impl.cpp b/gpr/source/lib/xmp_core/XMPCore_Impl.cpp
new file mode 100644
index 0000000..f98b717
--- /dev/null
+++ b/gpr/source/lib/xmp_core/XMPCore_Impl.cpp
@@ -0,0 +1,1390 @@
+// =================================================================================================
+// Copyright 2004 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.
+// =================================================================================================
+
+#include <algorithm>
+
+#include "public/include/XMP_Environment.h" // ! This must be the first include!
+#include "public/include/XMP_Version.h"
+#include "XMPCore_Impl.hpp"
+#include "XMPMeta.hpp" // *** For use of GetNamespacePrefix in FindSchemaNode.
+
+#include "UnicodeInlines.incl_cpp"
+
+using namespace std;
+
+#if XMP_WinBuild
+ #pragma warning ( disable : 4290 ) // C++ exception specification ignored except ... not __declspec(nothrow)
+ #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning)
+#endif
+
+// =================================================================================================
+// Static Variables
+// ================
+
+XMP_Int32 sXMP_InitCount = 0;
+
+XMP_NamespaceTable * sRegisteredNamespaces = 0;
+
+XMP_AliasMap * sRegisteredAliasMap = 0;
+
+void * voidVoidPtr = 0; // Used to backfill null output parameters.
+XMP_StringPtr voidStringPtr = 0;
+XMP_StringLen voidStringLen = 0;
+XMP_OptionBits voidOptionBits = 0;
+XMP_Uns8 voidByte = 0;
+bool voidBool = 0;
+XMP_Int32 voidInt32 = 0;
+XMP_Int64 voidInt64 = 0;
+double voidDouble = 0.0;
+XMP_DateTime voidDateTime;
+WXMP_Result void_wResult;
+
+// =================================================================================================
+// Local Utilities
+// ===============
+
+// -------------------------------------------------------------------------------------------------
+// VerifyXPathRoot
+// ---------------
+//
+// Set up the first 2 components of the expanded XPath. Normalizes the various cases of using the
+// full schema URI and/or a qualified root property name. Returns true for normal processing. If
+// allowUnknownSchemaNS is true and the schema namespace is not registered, false is returned. If
+// allowUnknownSchemaNS is false and the schema namespace is not registered, an exception is thrown.
+
+// *** Should someday check the full syntax.
+
+static void
+VerifyXPathRoot ( XMP_StringPtr schemaURI,
+ XMP_StringPtr propName,
+ XMP_ExpandedXPath * expandedXPath )
+{
+ // Do some basic checks on the URI and name. Try to lookup the URI. See if the name is qualified.
+
+ XMP_Assert ( (schemaURI != 0) && (propName != 0) && (*propName != 0) );
+ XMP_Assert ( (expandedXPath != 0) && (expandedXPath->empty()) );
+
+ if ( *schemaURI == 0 ) XMP_Throw ( "Schema namespace URI is required", kXMPErr_BadSchema );
+
+ if ( (*propName == '?') || (*propName == '@') ) {
+ XMP_Throw ( "Top level name must not be a qualifier", kXMPErr_BadXPath );
+ }
+ for ( XMP_StringPtr ch = propName; *ch != 0; ++ch ) {
+ if ( (*ch == '/') || (*ch == '[') ) {
+ XMP_Throw ( "Top level name must be simple", kXMPErr_BadXPath );
+ }
+ }
+
+ XMP_StringPtr schemaPrefix;
+ bool nsFound = sRegisteredNamespaces->GetPrefix ( schemaURI, &schemaPrefix, 0 );
+ if ( ! nsFound ) XMP_Throw ( "Unregistered schema namespace URI", kXMPErr_BadSchema );
+
+ XMP_StringPtr colonPos = propName;
+ while ( (*colonPos != 0) && (*colonPos != ':') ) ++colonPos;
+ VerifySimpleXMLName ( propName, colonPos ); // Verify the part before any colon.
+
+ // Verify the various URI and prefix combinations. Initialize the expanded XPath.
+
+ if ( *colonPos == 0 ) {
+
+ // The propName is unqualified, use the schemaURI and associated prefix.
+
+ expandedXPath->push_back ( XPathStepInfo ( schemaURI, kXMP_SchemaNode ) );
+ expandedXPath->push_back ( XPathStepInfo ( schemaPrefix, 0 ) );
+ (*expandedXPath)[kRootPropStep].step += propName;
+
+ } else {
+
+ // The propName is qualified. Make sure the prefix is legit. Use the associated URI and qualified name.
+
+ size_t prefixLen = colonPos - propName + 1; // ! Include the colon.
+ VerifySimpleXMLName ( colonPos+1, colonPos+strlen(colonPos) );
+
+ XMP_VarString prefix ( propName, prefixLen );
+ if ( prefix != schemaPrefix ) XMP_Throw ( "Schema namespace URI and prefix mismatch", kXMPErr_BadSchema );
+
+ expandedXPath->push_back ( XPathStepInfo ( schemaURI, kXMP_SchemaNode ) );
+ expandedXPath->push_back ( XPathStepInfo ( propName, 0 ) );
+
+ }
+
+} // VerifyXPathRoot
+
+// -------------------------------------------------------------------------------------------------
+// VerifyQualName
+// --------------
+
+static void
+VerifyQualName ( XMP_StringPtr qualName, XMP_StringPtr nameEnd )
+{
+ if ( qualName >= nameEnd ) XMP_Throw ( "Empty qualified name", kXMPErr_BadXPath );
+
+ XMP_StringPtr colonPos = qualName;
+ while ( (colonPos < nameEnd) && (*colonPos != ':') ) ++colonPos;
+ if ( (colonPos == qualName) || (colonPos >= nameEnd) ) XMP_Throw ( "Ill-formed qualified name", kXMPErr_BadXPath );
+
+ VerifySimpleXMLName ( qualName, colonPos );
+ VerifySimpleXMLName ( colonPos+1, nameEnd );
+
+ size_t prefixLen = colonPos - qualName + 1; // ! Include the colon.
+ XMP_VarString prefix ( qualName, prefixLen );
+ bool nsFound = sRegisteredNamespaces->GetURI ( prefix.c_str(), 0, 0 );
+ if ( ! nsFound ) XMP_Throw ( "Unknown namespace prefix for qualified name", kXMPErr_BadXPath );
+
+} // VerifyQualName
+
+// -------------------------------------------------------------------------------------------------
+// FindIndexedItem
+// ---------------
+//
+// [index] An element of an array.
+//
+// Support the implicit creation of a new last item.
+
+static XMP_Index
+FindIndexedItem ( XMP_Node * arrayNode, const XMP_VarString & indexStep, bool createNodes )
+{
+ XMP_Index index = 0;
+ size_t chLim = indexStep.size() - 1;
+
+ XMP_Assert ( (chLim >= 2) && (indexStep[0] == '[') && (indexStep[chLim] == ']') );
+
+ for ( size_t chNum = 1; chNum != chLim; ++chNum ) {
+ XMP_Assert ( ('0' <= indexStep[chNum]) && (indexStep[chNum] <= '9') );
+ index = (index * 10) + (indexStep[chNum] - '0');
+ if ( index < 0 ) {
+ XMP_Throw ( "Array index overflow", kXMPErr_BadXPath ); // ! Overflow, not truly negative.
+ }
+ }
+
+ --index; // Change to a C-style, zero based index.
+ if ( index < 0 ) XMP_Throw ( "Array index must be larger than zero", kXMPErr_BadXPath );
+
+ if ( (index == (XMP_Index)arrayNode->children.size()) && createNodes ) { // Append a new last+1 node.
+ XMP_Node * newItem = new XMP_Node ( arrayNode, kXMP_ArrayItemName, kXMP_NewImplicitNode );
+ arrayNode->children.push_back ( newItem );
+ }
+
+ // ! Don't throw here for a too large index. SetProperty will throw, GetProperty will not.
+ if ( index >= (XMP_Index)arrayNode->children.size() ) index = -1;
+ return index;
+
+} // FindIndexedItem
+
+// -------------------------------------------------------------------------------------------------
+// SplitNameAndValue
+// -----------------
+//
+// Split the name and value parts for field and qualifier selectors:
+//
+// [qualName="value"] An element in an array of structs, chosen by a field value.
+// [?qualName="value"] An element in an array, chosen by a qualifier value.
+//
+// The value portion is a string quoted by ''' or '"'. The value may contain any character including
+// a doubled quoting character. The value may be empty.
+
+static void
+SplitNameAndValue ( const XMP_VarString & selStep, XMP_VarString * nameStr, XMP_VarString * valueStr )
+{
+ XMP_StringPtr partBegin = selStep.c_str();
+ XMP_StringPtr partEnd;
+
+ const XMP_StringPtr valueEnd = partBegin + (selStep.size() - 2);
+ const char quote = *valueEnd;
+
+ XMP_Assert ( (*partBegin == '[') && (*(valueEnd+1) == ']') );
+ XMP_Assert ( (selStep.size() >= 6) && ((quote == '"') || (quote == '\'')) );
+
+ // Extract the name part.
+
+ ++partBegin; // Skip the opening '['.
+ if ( *partBegin == '?' ) ++partBegin;
+ for ( partEnd = partBegin+1; *partEnd != '='; ++partEnd ) {};
+
+ nameStr->assign ( partBegin, (partEnd - partBegin) );
+
+ // Extract the value part, reducing doubled quotes.
+
+ XMP_Assert ( *(partEnd+1) == quote );
+
+ partBegin = partEnd + 2;
+ valueStr->erase();
+ valueStr->reserve ( valueEnd - partBegin ); // Maximum length, don't optimize doubled quotes.
+
+ for ( partEnd = partBegin; partEnd < valueEnd; ++partEnd ) {
+ if ( (*partEnd == quote) && (*(partEnd+1) == quote) ) {
+ ++partEnd;
+ valueStr->append ( partBegin, (partEnd - partBegin) );
+ partBegin = partEnd+1; // ! Loop will increment partEnd again.
+ }
+ }
+
+ valueStr->append ( partBegin, (partEnd - partBegin) ); // ! The loop does not add the last part.
+
+} // SplitNameAndValue
+
+// -------------------------------------------------------------------------------------------------
+// LookupQualSelector
+// ------------------
+//
+// [?qualName="value"] An element in an array, chosen by a qualifier value.
+//
+// Note that we don't create implicit nodes for qualifier selectors, so no CreateNodes parameter.
+
+static XMP_Index
+LookupQualSelector ( XMP_Node * arrayNode, const XMP_VarString & qualName, XMP_VarString & qualValue )
+{
+ XMP_Index index;
+
+ if ( qualName == "xml:lang" ) {
+
+ // *** Should check that the value is legit RFC 1766/3066.
+ NormalizeLangValue ( &qualValue );
+ index = LookupLangItem ( arrayNode, qualValue ) ;
+
+ } else {
+
+ XMP_Index itemLim;
+ for ( index = 0, itemLim = arrayNode->children.size(); index != itemLim; ++index ) {
+
+ const XMP_Node * currItem = arrayNode->children[index];
+ XMP_Assert ( currItem->parent == arrayNode );
+
+ size_t q, qualLim;
+ for ( q = 0, qualLim = currItem->qualifiers.size(); q != qualLim; ++q ) {
+ const XMP_Node * currQual = currItem->qualifiers[q];
+ XMP_Assert ( currQual->parent == currItem );
+ if ( currQual->name != qualName ) continue;
+ if ( currQual->value == qualValue ) break; // Exit qual loop.
+ }
+ if ( q != qualLim ) break; // Exit child loop, found an item with a matching qualifier.
+
+ }
+ if ( index == itemLim ) index = -1;
+
+ }
+
+ return index;
+
+} // LookupQualSelector
+
+// -------------------------------------------------------------------------------------------------
+// FollowXPathStep
+// ---------------
+//
+// After processing by ExpandXPath, a step can be of these forms:
+// qualName A top level property or struct field.
+// [index] An element of an array.
+// [last()] The last element of an array.
+// [qualName="value"] An element in an array of structs, chosen by a field value.
+// [?qualName="value"] An element in an array, chosen by a qualifier value.
+// ?qualName A general qualifier.
+//
+// Find the appropriate child node, resolving aliases, and optionally creating nodes.
+
+static XMP_Node *
+FollowXPathStep ( XMP_Node * parentNode,
+ const XMP_ExpandedXPath & fullPath,
+ size_t stepNum,
+ bool createNodes,
+ XMP_NodePtrPos * ptrPos,
+ bool aliasedArrayItem = false )
+{
+ XMP_Node * nextNode = 0;
+ const XPathStepInfo & nextStep = fullPath[stepNum];
+ XMP_Index index = 0;
+ XMP_OptionBits stepKind = nextStep.options & kXMP_StepKindMask;
+
+ XMP_Assert ( (kXMP_StructFieldStep <= stepKind) && (stepKind <= kXMP_FieldSelectorStep) );
+
+ if ( stepKind == kXMP_StructFieldStep ) {
+
+ nextNode = FindChildNode ( parentNode, nextStep.step.c_str(), createNodes, ptrPos );
+
+ } else if ( stepKind == kXMP_QualifierStep ) {
+
+ XMP_StringPtr qualStep = nextStep.step.c_str();
+ XMP_Assert ( *qualStep == '?' );
+ ++qualStep;
+ nextNode = FindQualifierNode ( parentNode, qualStep, createNodes, ptrPos );
+
+ } else {
+
+ // This is an array indexing step. First get the index, then get the node.
+
+ if ( ! (parentNode->options & kXMP_PropValueIsArray) ) {
+ XMP_Throw ( "Indexing applied to non-array", kXMPErr_BadXPath );
+ }
+
+ if ( stepKind == kXMP_ArrayIndexStep ) {
+ index = FindIndexedItem ( parentNode, nextStep.step, createNodes );
+ } else if ( stepKind == kXMP_ArrayLastStep ) {
+ index = parentNode->children.size() - 1;
+ } else if ( stepKind == kXMP_FieldSelectorStep ) {
+ XMP_VarString fieldName, fieldValue;
+ SplitNameAndValue ( nextStep.step, &fieldName, &fieldValue );
+ index = LookupFieldSelector ( parentNode, fieldName.c_str(), fieldValue.c_str() );
+ } else if ( stepKind == kXMP_QualSelectorStep ) {
+ XMP_VarString qualName, qualValue;
+ SplitNameAndValue ( nextStep.step, &qualName, &qualValue );
+ index = LookupQualSelector ( parentNode, qualName, qualValue );
+ } else {
+ XMP_Throw ( "Unknown array indexing step in FollowXPathStep", kXMPErr_InternalFailure );
+ }
+
+ if ( (0 <= index) && (index <= (XMP_Index)parentNode->children.size()) ) nextNode = parentNode->children[index];
+
+ if ( (index == -1) && createNodes && aliasedArrayItem && (stepKind == kXMP_QualSelectorStep) ) {
+
+ // An ugly special case without an obvious better place to be. We have an alias to the
+ // x-default item of an alt-text array. A simple reference via SetProperty must create
+ // the x-default item if it does not yet exist.
+
+ XMP_Assert ( parentNode->options & kXMP_PropArrayIsAltText );
+ XMP_Assert ( (stepNum == 2) && (nextStep.step == "[?xml:lang=\"x-default\"]") );
+
+ nextNode = new XMP_Node ( parentNode, kXMP_ArrayItemName,
+ (kXMP_PropHasQualifiers | kXMP_PropHasLang | kXMP_NewImplicitNode) );
+
+ XMP_Node * langQual = new XMP_Node ( nextNode, "xml:lang", "x-default", kXMP_PropIsQualifier );
+ nextNode->qualifiers.push_back ( langQual );
+
+ if ( parentNode->children.empty() ) {
+ parentNode->children.push_back ( nextNode );
+ } else {
+ parentNode->children.insert ( parentNode->children.begin(), nextNode );
+ }
+
+ index = 0; // ! C-style index! The x-default item is always first.
+
+ }
+
+ if ( (nextNode != 0) && (ptrPos != 0) ) *ptrPos = parentNode->children.begin() + index;
+
+ }
+
+ if ( (nextNode != 0) && (nextNode->options & kXMP_NewImplicitNode) ) {
+ nextNode->options |= (nextStep.options & kXMP_PropArrayFormMask);
+ }
+
+ XMP_Assert ( (ptrPos == 0) || (nextNode == 0) || (nextNode == **ptrPos) );
+ XMP_Assert ( (nextNode != 0) || (! createNodes) );
+ return nextNode;
+
+} // FollowXPathStep
+
+// -------------------------------------------------------------------------------------------------
+// CheckImplicitStruct
+// -------------------
+
+static inline void
+CheckImplicitStruct ( XMP_Node * node,
+ const XMP_ExpandedXPath & expandedXPath,
+ size_t stepNum,
+ size_t stepLim )
+{
+
+ if ( (stepNum < stepLim) &&
+ ((node->options & kXMP_PropCompositeMask) == 0) &&
+ (GetStepKind ( expandedXPath[stepNum].options ) == kXMP_StructFieldStep) ) {
+
+ node->options |= kXMP_PropValueIsStruct;
+
+ }
+
+} // CheckImplicitStruct
+
+// -------------------------------------------------------------------------------------------------
+// DeleteSubtree
+// -------------
+
+void
+DeleteSubtree ( XMP_NodePtrPos rootNodePos )
+{
+ XMP_Node * rootNode = *rootNodePos;
+ XMP_Node * rootParent = rootNode->parent;
+
+ if ( ! (rootNode->options & kXMP_PropIsQualifier) ) {
+
+ rootParent->children.erase ( rootNodePos );
+
+ } else {
+
+ rootParent->qualifiers.erase ( rootNodePos );
+
+ XMP_Assert ( rootParent->options & kXMP_PropHasQualifiers);
+ if ( rootParent->qualifiers.empty() ) rootParent->options ^= kXMP_PropHasQualifiers;
+
+ if ( rootNode->name == "xml:lang" ) {
+ XMP_Assert ( rootParent->options & kXMP_PropHasLang);
+ rootParent->options ^= kXMP_PropHasLang;
+ } else if ( rootNode->name == "rdf:type" ) {
+ XMP_Assert ( rootParent->options & kXMP_PropHasType);
+ rootParent->options ^= kXMP_PropHasType;
+ }
+
+ }
+
+ delete rootNode;
+
+} // DeleteSubtree
+
+// =================================================================================================
+// =================================================================================================
+
+// =================================================================================================
+// VerifySetOptions
+// ================
+//
+// Normalize and verify the option flags for SetProperty and similar functions. The allowed options
+// here are just those that apply to the property, that would be kept in the XMP_Node. Others that
+// affect the selection of the node or other processing must be removed by now. These are:
+// kXMP_InsertBeforeItem
+// kXMP_InsertAfterItem
+// kXMP_KeepQualifiers
+// kXMPUtil_AllowCommas
+
+enum {
+ kXMP_AllSetOptionsMask = (kXMP_PropValueIsURI |
+ kXMP_PropValueIsStruct |
+ kXMP_PropValueIsArray |
+ kXMP_PropArrayIsOrdered |
+ kXMP_PropArrayIsAlternate |
+ kXMP_PropArrayIsAltText |
+ kXMP_DeleteExisting)
+};
+
+XMP_OptionBits
+VerifySetOptions ( XMP_OptionBits options, XMP_StringPtr propValue )
+{
+
+ if ( options & kXMP_PropArrayIsAltText ) options |= kXMP_PropArrayIsAlternate;
+ if ( options & kXMP_PropArrayIsAlternate ) options |= kXMP_PropArrayIsOrdered;
+ if ( options & kXMP_PropArrayIsOrdered ) options |= kXMP_PropValueIsArray;
+
+ if ( options & ~kXMP_AllSetOptionsMask ) {
+ XMP_Throw ( "Unrecognized option flags", kXMPErr_BadOptions );
+ }
+
+ if ( (options & kXMP_PropValueIsStruct) && (options & kXMP_PropValueIsArray) ) {
+ XMP_Throw ( "IsStruct and IsArray options are mutually exclusive", kXMPErr_BadOptions );
+ }
+
+ if ( (options & kXMP_PropValueOptionsMask) && (options & kXMP_PropCompositeMask) ) {
+ XMP_Throw ( "Structs and arrays can't have \"value\" options", kXMPErr_BadOptions );
+ }
+
+ if ( (propValue != 0) && (options & kXMP_PropCompositeMask) ) {
+ XMP_Throw ( "Structs and arrays can't have string values", kXMPErr_BadOptions );
+ }
+
+ return options;
+
+} // VerifySetOptions
+
+// =================================================================================================
+// ComposeXPath
+// ============
+//
+// Compose the canonical string form of an expanded XPath expression.
+
+extern void
+ComposeXPath ( const XMP_ExpandedXPath & expandedXPath,
+ XMP_VarString * stringXPath )
+{
+ *stringXPath = expandedXPath[kRootPropStep].step;
+
+ for ( size_t index = kRootPropStep+1; index < expandedXPath.size(); ++index ) {
+ const XPathStepInfo & currStep = expandedXPath[index];
+
+ switch ( currStep.options & kXMP_StepKindMask ) {
+
+ case kXMP_StructFieldStep :
+ case kXMP_QualifierStep :
+ *stringXPath += '/';
+ *stringXPath += currStep.step;
+ break;
+
+ case kXMP_ArrayIndexStep :
+ case kXMP_ArrayLastStep :
+ case kXMP_QualSelectorStep :
+ case kXMP_FieldSelectorStep :
+ *stringXPath += currStep.step;
+ break;
+
+ default:
+ XMP_Throw ( "Unexpected", kXMPErr_InternalFailure );
+
+ }
+
+ }
+
+} // ComposeXPath
+
+// =================================================================================================
+// ExpandXPath
+// ===========
+//
+// Split an XPath expression apart at the conceptual steps, adding the root namespace prefix to the
+// first property component. The schema URI is put in the first (0th) slot in the expanded XPath.
+// Check if the top level component is an alias, but don't resolve it.
+//
+// In the most verbose case steps are separated by '/', and each step can be of these forms:
+//
+// qualName A top level property or struct field.
+// *[index] An element of an array.
+// *[last()] The last element of an array.
+// *[fieldName="value"] An element in an array of structs, chosen by a field value.
+// *[@xml:lang="value"] An element in an alt-text array, chosen by the xml:lang qualifier.
+// *[?qualName="value"] An element in an array, chosen by a qualifier value.
+// @xml:lang An xml:lang qualifier.
+// ?qualName A general qualifier.
+//
+// The logic is complicated though by shorthand for arrays, the separating '/' and leading '*'
+// are optional. These are all equivalent: array/*[2] array/[2] array*[2] array[2]
+// All of these are broken into the 2 steps "array" and "[2]".
+//
+// The value portion in the array selector forms is a string quoted by ''' or '"'. The value
+// may contain any character including a doubled quoting character. The value may be empty.
+//
+// The syntax isn't checked, but an XML name begins with a letter or '_', and contains letters,
+// digits, '.', '-', '_', and a bunch of special non-ASCII Unicode characters. An XML qualified
+// name is a pair of names separated by a colon.
+
+void
+ExpandXPath ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propPath,
+ XMP_ExpandedXPath * expandedXPath )
+{
+ XMP_Assert ( (schemaNS != 0) && (propPath != 0) && (*propPath != 0) && (expandedXPath != 0) );
+
+ XMP_StringPtr stepBegin, stepEnd;
+ XMP_StringPtr qualName, nameEnd;
+ XMP_VarString currStep;
+
+ size_t resCount = 2; // Guess at the number of steps. At least 2, plus 1 for each '/' or '['.
+ for ( stepEnd = propPath; *stepEnd != 0; ++stepEnd ) {
+ if ( (*stepEnd == '/') || (*stepEnd == '[') ) ++resCount;
+ }
+
+ expandedXPath->clear();
+ expandedXPath->reserve ( resCount );
+
+ // -------------------------------------------------------------------------------------------
+ // Pull out the first component and do some special processing on it: add the schema namespace
+ // prefix and see if it is an alias. The start must be a qualName.
+
+ stepBegin = propPath;
+ stepEnd = stepBegin;
+ while ( (*stepEnd != 0) && (*stepEnd != '/') && (*stepEnd != '[') && (*stepEnd != '*') ) ++stepEnd;
+ if ( stepEnd == stepBegin ) XMP_Throw ( "Empty initial XPath step", kXMPErr_BadXPath );
+ currStep.assign ( stepBegin, (stepEnd - stepBegin) );
+
+ VerifyXPathRoot ( schemaNS, currStep.c_str(), expandedXPath );
+
+ XMP_OptionBits stepFlags = kXMP_StructFieldStep;
+ if ( sRegisteredAliasMap->find ( (*expandedXPath)[kRootPropStep].step ) != sRegisteredAliasMap->end() ) {
+ stepFlags |= kXMP_StepIsAlias;
+ }
+ (*expandedXPath)[kRootPropStep].options |= stepFlags;
+
+ // -----------------------------------------------------
+ // Now continue to process the rest of the XPath string.
+
+ while ( *stepEnd != 0 ) {
+
+ stepBegin = stepEnd;
+ if ( *stepBegin == '/' ) ++stepBegin;
+ if ( *stepBegin == '*' ) {
+ ++stepBegin;
+ if ( *stepBegin != '[' ) XMP_Throw ( "Missing '[' after '*'", kXMPErr_BadXPath );
+ }
+ stepEnd = stepBegin;
+
+ if ( *stepBegin != '[' ) {
+
+ // A struct field or qualifier.
+ qualName = stepBegin;
+ while ( (*stepEnd != 0) && (*stepEnd != '/') && (*stepEnd != '[') && (*stepEnd != '*') ) ++stepEnd;
+ nameEnd = stepEnd;
+ stepFlags = kXMP_StructFieldStep; // ! Touch up later, also changing '@' to '?'.
+
+ } else {
+
+ // One of the array forms.
+
+ ++stepEnd; // Look at the character after the leading '['.
+
+ if ( ('0' <= *stepEnd) && (*stepEnd <= '9') ) {
+
+ // A numeric (decimal integer) array index.
+ while ( (*stepEnd != 0) && ('0' <= *stepEnd) && (*stepEnd <= '9') ) ++stepEnd;
+ if ( *stepEnd != ']' ) XMP_Throw ( "Missing ']' for integer array index", kXMPErr_BadXPath );
+ stepFlags = kXMP_ArrayIndexStep;
+
+ } else {
+
+ // Could be "[last()]" or one of the selector forms. Find the ']' or '='.
+
+ while ( (*stepEnd != 0) && (*stepEnd != ']') && (*stepEnd != '=') ) ++stepEnd;
+ if ( *stepEnd == 0 ) XMP_Throw ( "Missing ']' or '=' for array index", kXMPErr_BadXPath );
+
+ if ( *stepEnd == ']' ) {
+
+ if ( strncmp ( "[last()", stepBegin, (stepEnd - stepBegin) ) != 0 ) {
+ XMP_Throw ( "Invalid non-numeric array index", kXMPErr_BadXPath );
+ }
+ stepFlags = kXMP_ArrayLastStep;
+
+ } else {
+
+ qualName = stepBegin+1;
+ nameEnd = stepEnd;
+ ++stepEnd; // Absorb the '=', remember the quote.
+ const char quote = *stepEnd;
+ if ( (quote != '\'') && (quote != '"') ) {
+ XMP_Throw ( "Invalid quote in array selector", kXMPErr_BadXPath );
+ }
+
+ ++stepEnd; // Absorb the leading quote.
+ while ( *stepEnd != 0 ) {
+ if ( *stepEnd == quote ) {
+ if ( *(stepEnd+1) != quote ) break;
+ ++stepEnd;
+ }
+ ++stepEnd;
+ }
+ if ( *stepEnd == 0 ) {
+ XMP_Throw ( "No terminating quote for array selector", kXMPErr_BadXPath );
+ }
+ ++stepEnd; // Absorb the trailing quote.
+
+ stepFlags = kXMP_FieldSelectorStep; // ! Touch up later, also changing '@' to '?'.
+
+ }
+
+ }
+
+ if ( *stepEnd != ']' ) XMP_Throw ( "Missing ']' for array index", kXMPErr_BadXPath );
+ ++stepEnd;
+
+ }
+
+ if ( stepEnd == stepBegin ) XMP_Throw ( "Empty XPath step", kXMPErr_BadXPath );
+ currStep.assign ( stepBegin, (stepEnd - stepBegin) );
+
+ if ( GetStepKind ( stepFlags ) == kXMP_StructFieldStep ) {
+
+ if ( currStep[0] == '@' ) {
+ currStep[0] = '?';
+ if ( currStep != "?xml:lang" ) XMP_Throw ( "Only xml:lang allowed with '@'", kXMPErr_BadXPath );
+ }
+ if ( currStep[0] == '?' ) {
+ ++qualName;
+ stepFlags = kXMP_QualifierStep;
+ }
+ VerifyQualName ( qualName, nameEnd );
+
+ } else if ( GetStepKind ( stepFlags ) == kXMP_FieldSelectorStep ) {
+
+ if ( currStep[1] == '@' ) {
+ currStep[1] = '?';
+ if ( strncmp ( currStep.c_str(), "[?xml:lang=", 11 ) != 0 ) {
+ XMP_Throw ( "Only xml:lang allowed with '@'", kXMPErr_BadXPath );
+ }
+ }
+ if ( currStep[1] == '?' ) {
+ ++qualName;
+ stepFlags = kXMP_QualSelectorStep;
+ }
+ VerifyQualName ( qualName, nameEnd );
+
+ }
+
+ expandedXPath->push_back ( XPathStepInfo ( currStep, stepFlags ) );
+
+ }
+
+} // ExpandXPath
+
+// =================================================================================================
+// FindSchemaNode
+// ==============
+//
+// Find or create a schema node. Returns a pointer to the node, and optionally an iterator for the
+// node's position in the top level vector of schema nodes. The iterator is unchanged if no schema
+// node (null) is returned.
+
+XMP_Node *
+FindSchemaNode ( XMP_Node * xmpTree,
+ XMP_StringPtr nsURI,
+ bool createNodes,
+ XMP_NodePtrPos * ptrPos /* = 0 */ )
+{
+ XMP_Node * schemaNode = 0;
+
+ XMP_Assert ( xmpTree->parent == 0 );
+
+ for ( size_t schemaNum = 0, schemaLim = xmpTree->children.size(); schemaNum != schemaLim; ++schemaNum ) {
+ XMP_Node * currSchema = xmpTree->children[schemaNum];
+ XMP_Assert ( currSchema->parent == xmpTree );
+ if ( currSchema->name == nsURI ) {
+ schemaNode = currSchema;
+ if ( ptrPos != 0 ) *ptrPos = xmpTree->children.begin() + schemaNum;
+ break;
+ }
+ }
+
+ if ( (schemaNode == 0) && createNodes ) {
+
+ schemaNode = new XMP_Node ( xmpTree, nsURI, (kXMP_SchemaNode | kXMP_NewImplicitNode) );
+
+ try {
+ XMP_StringPtr prefixPtr;
+ XMP_StringLen prefixLen;
+ bool found = XMPMeta::GetNamespacePrefix ( nsURI, &prefixPtr, &prefixLen ); // *** Use map directly?
+ XMP_Assert ( found );
+ schemaNode->value.assign ( prefixPtr, prefixLen );
+ } catch (...) { // Don't leak schemaNode in case of an exception before adding it to the children vector.
+ delete schemaNode;
+ throw;
+ }
+
+ xmpTree->children.push_back ( schemaNode );
+ if ( ptrPos != 0 ) *ptrPos = xmpTree->children.end() - 1;
+
+ #if 0 // *** XMP_DebugBuild
+ schemaNode->_valuePtr = schemaNode->value.c_str();
+ #endif
+
+ }
+
+ XMP_Assert ( (ptrPos == 0) || (schemaNode == 0) || (schemaNode == **ptrPos) );
+ XMP_Assert ( (schemaNode != 0) || (! createNodes) );
+ return schemaNode;
+
+} // FindSchemaNode
+
+// =================================================================================================
+// FindChildNode
+// =============
+//
+// Find or create a child node under a given parent node. Returns a pointer to the child node, and
+// optionally an iterator for the node's position in the parent's vector of children. The iterator
+// is unchanged if no child node (null) is returned.
+
+XMP_Node *
+FindChildNode ( XMP_Node * parent,
+ XMP_StringPtr childName,
+ bool createNodes,
+ XMP_NodePtrPos * ptrPos /* = 0 */ )
+{
+ XMP_Node * childNode = 0;
+
+ if ( ! (parent->options & (kXMP_SchemaNode | kXMP_PropValueIsStruct)) ) {
+ if ( ! (parent->options & kXMP_NewImplicitNode) ) {
+ XMP_Throw ( "Named children only allowed for schemas and structs", kXMPErr_BadXPath );
+ }
+ if ( parent->options & kXMP_PropValueIsArray ) {
+ XMP_Throw ( "Named children not allowed for arrays", kXMPErr_BadXPath );
+ }
+ if ( ! createNodes ) { // *** Should be assert? If !createNodes, why is the parent a new implicit node?
+ XMP_Throw ( "Parent is new implicit node, but createNodes is false", kXMPErr_InternalFailure );
+ }
+ parent->options |= kXMP_PropValueIsStruct;
+ }
+
+ for ( size_t childNum = 0, childLim = parent->children.size(); childNum != childLim; ++childNum ) {
+ XMP_Node * currChild = parent->children[childNum];
+ XMP_Assert ( currChild->parent == parent );
+ if ( currChild->name == childName ) {
+ childNode = currChild;
+ if ( ptrPos != 0 ) *ptrPos = parent->children.begin() + childNum;
+ break;
+ }
+ }
+
+ if ( (childNode == 0) && createNodes ) {
+ childNode = new XMP_Node ( parent, childName, kXMP_NewImplicitNode );
+ parent->children.push_back ( childNode );
+ if ( ptrPos != 0 ) *ptrPos = parent->children.end() - 1;
+ }
+
+ XMP_Assert ( (ptrPos == 0) || (childNode == 0) || (childNode == **ptrPos) );
+ XMP_Assert ( (childNode != 0) || (! createNodes) );
+ return childNode;
+
+} // FindChildNode
+
+// =================================================================================================
+// FindQualifierNode
+// =================
+//
+// Find or create a qualifier node under a given parent node. Returns a pointer to the qualifier node,
+// and optionally an iterator for the node's position in the parent's vector of qualifiers. The iterator
+// is unchanged if no qualifier node (null) is returned.
+//
+// ! On entry, the qualName parameter must not have the leading '?' from the XPath step.
+
+XMP_Node *
+FindQualifierNode ( XMP_Node * parent,
+ XMP_StringPtr qualName,
+ bool createNodes,
+ XMP_NodePtrPos * ptrPos /* = 0 */ ) // *** Require ptrPos internally & remove checks?
+{
+ XMP_Node * qualNode = 0;
+
+ XMP_Assert ( *qualName != '?' );
+
+ for ( size_t qualNum = 0, qualLim = parent->qualifiers.size(); qualNum != qualLim; ++qualNum ) {
+ XMP_Node * currQual = parent->qualifiers[qualNum];
+ XMP_Assert ( currQual->parent == parent );
+ if ( currQual->name == qualName ) {
+ qualNode = currQual;
+ if ( ptrPos != 0 ) *ptrPos = parent->qualifiers.begin() + qualNum;
+ break;
+ }
+ }
+
+ if ( (qualNode == 0) && createNodes ) {
+
+ qualNode = new XMP_Node ( parent, qualName, (kXMP_PropIsQualifier | kXMP_NewImplicitNode) );
+ parent->options |= kXMP_PropHasQualifiers;
+
+ const bool isLang = XMP_LitMatch ( qualName, "xml:lang" );
+ const bool isType = XMP_LitMatch ( qualName, "rdf:type" );
+ const bool isSpecial = isLang | isType;
+
+ if ( isLang ) {
+ parent->options |= kXMP_PropHasLang;
+ } else if ( isType ) {
+ parent->options |= kXMP_PropHasType;
+ }
+
+ if ( parent->qualifiers.empty() || (! isSpecial) ) {
+ parent->qualifiers.push_back ( qualNode );
+ if ( ptrPos != 0 ) *ptrPos = parent->qualifiers.end() - 1;
+ } else {
+ XMP_NodePtrPos insertPos = parent->qualifiers.begin(); // ! Lang goes first, type after.
+ if ( isType && (parent->options & kXMP_PropHasLang) ) ++insertPos; // *** Does insert at end() work?
+ insertPos = parent->qualifiers.insert ( insertPos, qualNode );
+ if ( ptrPos != 0 ) *ptrPos = insertPos;
+ }
+
+ }
+
+ XMP_Assert ( (ptrPos == 0) || (qualNode == 0) || (qualNode == **ptrPos) );
+ XMP_Assert ( (qualNode != 0) || (! createNodes) );
+ return qualNode;
+
+} // FindQualifierNode
+
+// =================================================================================================
+// LookupFieldSelector
+// ===================
+//
+// [fieldName="value"] An element in an array of structs, chosen by a field value.
+//
+// Note that we don't create implicit nodes for field selectors, so no CreateNodes parameter.
+
+XMP_Index
+LookupFieldSelector ( const XMP_Node * arrayNode, XMP_StringPtr fieldName, XMP_StringPtr fieldValue )
+{
+ XMP_Index index, itemLim;
+
+ for ( index = 0, itemLim = arrayNode->children.size(); index != itemLim; ++index ) {
+
+ const XMP_Node * currItem = arrayNode->children[index];
+ XMP_Assert ( currItem->parent == arrayNode );
+
+ if ( ! (currItem->options & kXMP_PropValueIsStruct) ) {
+ XMP_Throw ( "Field selector must be used on array of struct", kXMPErr_BadXPath );
+ }
+
+ size_t f, fieldLim;
+ for ( f = 0, fieldLim = currItem->children.size(); f != fieldLim; ++f ) {
+ const XMP_Node * currField = currItem->children[f];
+ XMP_Assert ( currField->parent == currItem );
+ if ( currField->name != fieldName ) continue;
+ if ( currField->value == fieldValue ) break; // Exit qual loop.
+ }
+ if ( f != fieldLim ) break; // Exit child loop, found an item with a matching qualifier.
+
+ }
+
+ if ( index == itemLim ) index = -1;
+ return index;
+
+} // LookupFieldSelector
+
+// =================================================================================================
+// LookupLangItem
+// ==============
+//
+// ! Assumes that the language value is already normalized.
+
+XMP_Index
+LookupLangItem ( const XMP_Node * arrayNode, XMP_VarString & lang )
+{
+ if ( ! (arrayNode->options & kXMP_PropValueIsArray) ) { // *** Check for alt-text?
+ XMP_Throw ( "Language item must be used on array", kXMPErr_BadXPath );
+ }
+
+ XMP_Index index = 0;
+ XMP_Index itemLim = arrayNode->children.size();
+
+ for ( ; index != itemLim; ++index ) {
+ const XMP_Node * currItem = arrayNode->children[index];
+ XMP_Assert ( currItem->parent == arrayNode );
+ if ( currItem->qualifiers.empty() || (currItem->qualifiers[0]->name != "xml:lang") ) continue;
+ if ( currItem->qualifiers[0]->value == lang ) break;
+ }
+
+ if ( index == itemLim ) index = -1;
+ return index;
+
+} // LookupLangItem
+
+// =================================================================================================
+// FindNode
+// ========
+//
+// Follow an expanded path expression to find or create a node. Returns a pointer to the node, and
+// optionally an iterator for the node's position in the parent's vector of children or qualifiers.
+// The iterator is unchanged if no child node (null) is returned.
+
+XMP_Node *
+FindNode ( XMP_Node * xmpTree,
+ const XMP_ExpandedXPath & expandedXPath,
+ bool createNodes,
+ XMP_OptionBits leafOptions /* = 0 */,
+ XMP_NodePtrPos * ptrPos /* = 0 */ )
+{
+ XMP_Node * currNode = 0;
+ XMP_NodePtrPos currPos;
+ XMP_NodePtrPos newSubPos; // Root of implicitly created subtree. Valid only if leaf is new.
+ bool leafIsNew = false;
+
+ XMP_Assert ( (leafOptions == 0) || createNodes );
+
+ if ( expandedXPath.empty() ) XMP_Throw ( "Empty XPath", kXMPErr_BadXPath );
+
+ size_t stepNum = 1; // By default start calling FollowXPathStep for the top level property step.
+ size_t stepLim = expandedXPath.size();
+
+ // The start of processing deals with the schema node and top level alias. If the top level step
+ // is not an alias, lookup the expanded path's schema URI. Otherwise, lookup the expanded path
+ // for the actual. While tempting, don't substitute the actual's path into the local one, don't
+ // risk messing with the caller's use of that. Also don't call FindNode recursively, we need to
+ // keep track of the root of the implicitly created subtree as we move down the path.
+
+ if ( ! (expandedXPath[kRootPropStep].options & kXMP_StepIsAlias) ) {
+
+ currNode = FindSchemaNode ( xmpTree, expandedXPath[kSchemaStep].step.c_str(), createNodes, &currPos );
+ if ( currNode == 0 ) return 0;
+
+ if ( currNode->options & kXMP_NewImplicitNode ) {
+ currNode->options ^= kXMP_NewImplicitNode; // Clear the implicit node bit.
+ if ( ! leafIsNew ) newSubPos = currPos; // Save the top most implicit node.
+ leafIsNew = true; // If any parent is new, the leaf will be new also.
+ }
+
+ } else {
+
+ stepNum = 2; // ! Continue processing the original path at the second level step.
+
+ XMP_AliasMapPos aliasPos = sRegisteredAliasMap->find ( expandedXPath[kRootPropStep].step );
+ XMP_Assert ( aliasPos != sRegisteredAliasMap->end() );
+
+ currNode = FindSchemaNode ( xmpTree, aliasPos->second[kSchemaStep].step.c_str(), createNodes, &currPos );
+ if ( currNode == 0 ) goto EXIT;
+ if ( currNode->options & kXMP_NewImplicitNode ) {
+ currNode->options ^= kXMP_NewImplicitNode; // Clear the implicit node bit.
+ if ( ! leafIsNew ) newSubPos = currPos; // Save the top most implicit node.
+ leafIsNew = true; // If any parent is new, the leaf will be new also.
+ }
+
+ currNode = FollowXPathStep ( currNode, aliasPos->second, 1, createNodes, &currPos );
+ if ( currNode == 0 ) goto EXIT;
+ if ( currNode->options & kXMP_NewImplicitNode ) {
+ currNode->options ^= kXMP_NewImplicitNode; // Clear the implicit node bit.
+ CheckImplicitStruct ( currNode, expandedXPath, 2, stepLim );
+ if ( ! leafIsNew ) newSubPos = currPos; // Save the top most implicit node.
+ leafIsNew = true; // If any parent is new, the leaf will be new also.
+ }
+
+ XMP_OptionBits arrayForm = aliasPos->second[kRootPropStep].options & kXMP_PropArrayFormMask;
+ XMP_Assert ( (arrayForm == 0) || (arrayForm & kXMP_PropValueIsArray) );
+ XMP_Assert ( (arrayForm == 0) ? (aliasPos->second.size() == 2) : (aliasPos->second.size() == 3) );
+
+ if ( arrayForm != 0 ) {
+ currNode = FollowXPathStep ( currNode, aliasPos->second, 2, createNodes, &currPos, true );
+ if ( currNode == 0 ) goto EXIT;
+ if ( currNode->options & kXMP_NewImplicitNode ) {
+ currNode->options ^= kXMP_NewImplicitNode; // Clear the implicit node bit.
+ CheckImplicitStruct ( currNode, expandedXPath, 2, stepLim );
+ if ( ! leafIsNew ) newSubPos = currPos; // Save the top most implicit node.
+ leafIsNew = true; // If any parent is new, the leaf will be new also.
+ }
+ }
+
+ }
+
+ // Now follow the remaining steps of the original XPath.
+
+ // *** ??? Change all the num/lim loops back to num<lim? Probably safer.
+
+ try {
+ for ( ; stepNum < stepLim; ++stepNum ) {
+ currNode = FollowXPathStep ( currNode, expandedXPath, stepNum, createNodes, &currPos );
+ if ( currNode == 0 ) goto EXIT;
+ if ( currNode->options & kXMP_NewImplicitNode ) {
+ currNode->options ^= kXMP_NewImplicitNode; // Clear the implicit node bit.
+ CheckImplicitStruct ( currNode, expandedXPath, stepNum+1, stepLim );
+ if ( ! leafIsNew ) newSubPos = currPos; // Save the top most implicit node.
+ leafIsNew = true; // If any parent is new, the leaf will be new also.
+ }
+ }
+ } catch ( ... ) {
+ if ( leafIsNew ) DeleteSubtree ( newSubPos );
+ throw;
+ }
+
+ // Done. Delete the implicitly created subtree if the eventual node was not found.
+
+EXIT:
+
+ XMP_Assert ( (currNode == 0) || (currNode == *currPos) );
+ XMP_Assert ( (currNode != 0) || (! createNodes) );
+
+ if ( leafIsNew ) {
+ if ( currNode != 0 ) {
+ currNode->options |= leafOptions;
+ } else {
+ DeleteSubtree ( newSubPos );
+ }
+ }
+
+ if ( (currNode != 0) && (ptrPos != 0) ) *ptrPos = currPos;
+ return currNode;
+
+} // FindNode
+
+// =================================================================================================
+// CloneOffspring
+// ==============
+
+void
+CloneOffspring ( const XMP_Node * origParent, XMP_Node * cloneParent, bool skipEmpty /* = false */ )
+{
+ size_t qualCount = origParent->qualifiers.size();
+ size_t childCount = origParent->children.size();
+
+ if ( qualCount > 0 ) {
+
+ cloneParent->qualifiers.reserve ( qualCount );
+
+ for ( size_t qualNum = 0, qualLim = qualCount; qualNum != qualLim; ++qualNum ) {
+ const XMP_Node * origQual = origParent->qualifiers[qualNum];
+ if ( skipEmpty && origQual->value.empty() && origQual->children.empty() ) continue;
+ XMP_Node * cloneQual = new XMP_Node ( cloneParent, origQual->name, origQual->value, origQual->options );
+ CloneOffspring ( origQual, cloneQual, skipEmpty );
+ if ( skipEmpty && cloneQual->value.empty() && cloneQual->children.empty() ) {
+ // Check again, might have had an array or struct with all empty children.
+ delete cloneQual;
+ continue;
+ }
+ cloneParent->qualifiers.push_back ( cloneQual );
+ }
+
+ }
+
+ if ( childCount > 0 ) {
+
+ cloneParent->children.reserve ( childCount );
+
+ for ( size_t childNum = 0, childLim = childCount; childNum != childLim; ++childNum ) {
+ const XMP_Node * origChild = origParent->children[childNum];
+ if ( skipEmpty && origChild->value.empty() && origChild->children.empty() ) continue;
+ XMP_Node * cloneChild = new XMP_Node ( cloneParent, origChild->name, origChild->value, origChild->options );
+ CloneOffspring ( origChild, cloneChild, skipEmpty );
+ if ( skipEmpty && cloneChild->value.empty() && cloneChild->children.empty() ) {
+ // Check again, might have had an array or struct with all empty children.
+ delete cloneChild;
+ continue;
+ }
+ cloneParent->children.push_back ( cloneChild );
+ }
+
+ }
+
+} // CloneOffspring
+
+// =================================================================================================
+// CloneSubtree
+// ============
+
+XMP_Node *
+CloneSubtree ( const XMP_Node * origRoot, XMP_Node * cloneParent, bool skipEmpty /* = false */ )
+{
+ #if XMP_DebugBuild
+ if ( cloneParent->parent == 0 ) {
+ XMP_Assert ( origRoot->options & kXMP_SchemaNode );
+ XMP_Assert ( FindConstSchema ( cloneParent, origRoot->name.c_str() ) == 0 );
+ } else {
+ XMP_Assert ( ! (origRoot->options & kXMP_SchemaNode) );
+ if ( cloneParent->options & kXMP_PropValueIsStruct ) { // Might be an array.
+ XMP_Assert ( FindConstChild ( cloneParent, origRoot->name.c_str() ) == 0 );
+ }
+ }
+ #endif
+
+ XMP_Node * cloneRoot = new XMP_Node ( cloneParent, origRoot->name, origRoot->value, origRoot->options );
+ CloneOffspring ( origRoot, cloneRoot, skipEmpty ) ;
+
+ if ( skipEmpty && cloneRoot->value.empty() && cloneRoot->children.empty() ) {
+ // ! Can't do earlier, CloneOffspring might be skipping empty children.
+ delete cloneRoot;
+ return 0;
+ }
+
+ cloneParent->children.push_back ( cloneRoot );
+ return cloneRoot;
+
+} // CloneSubtree
+
+// =================================================================================================
+// CompareSubtrees
+// ===============
+//
+// Compare 2 subtrees for semantic equality. The comparison includes value, qualifiers, and form.
+// Schemas, top level properties, struct fields, and qualifiers are allowed to have differing order,
+// the appropriate right node is found from the left node's name. Alt-text arrays are allowed to be
+// in differing language order, other arrays are compared in order.
+
+// *** Might someday consider sorting unordered arrays.
+// *** Should expose this through XMPUtils.
+
+bool
+CompareSubtrees ( const XMP_Node & leftNode, const XMP_Node & rightNode )
+{
+ // Don't compare the names here, we want to allow the outermost roots to have different names.
+ if ( (leftNode.value != rightNode.value) ||
+ (leftNode.options != rightNode.options) ||
+ (leftNode.children.size() != rightNode.children.size()) ||
+ (leftNode.qualifiers.size() != rightNode.qualifiers.size()) ) return false;
+
+ // Compare the qualifiers, allowing them to be out of order.
+ for ( size_t qualNum = 0, qualLim = leftNode.qualifiers.size(); qualNum != qualLim; ++qualNum ) {
+ const XMP_Node * leftQual = leftNode.qualifiers[qualNum];
+ const XMP_Node * rightQual = FindConstQualifier ( &rightNode, leftQual->name.c_str() );
+ if ( (rightQual == 0) || (! CompareSubtrees ( *leftQual, *rightQual )) ) return false;
+ }
+
+ if ( (leftNode.parent == 0) || (leftNode.options & (kXMP_SchemaNode | kXMP_PropValueIsStruct)) ) {
+
+ // The parent node is a tree root, a schema, or a struct.
+ for ( size_t childNum = 0, childLim = leftNode.children.size(); childNum != childLim; ++childNum ) {
+ const XMP_Node * leftChild = leftNode.children[childNum];
+ const XMP_Node * rightChild = FindConstChild ( &rightNode, leftChild->name.c_str() );
+ if ( (rightChild == 0) || (! CompareSubtrees ( *leftChild, *rightChild )) ) return false;
+ }
+
+ } else if ( leftNode.options & kXMP_PropArrayIsAltText ) {
+
+ // The parent node is an alt-text array.
+ for ( size_t childNum = 0, childLim = leftNode.children.size(); childNum != childLim; ++childNum ) {
+ const XMP_Node * leftChild = leftNode.children[childNum];
+ XMP_Assert ( (! leftChild->qualifiers.empty()) && (leftChild->qualifiers[0]->name == "xml:lang") );
+ XMP_Index rightIndex = LookupLangItem ( &rightNode, leftChild->qualifiers[0]->value );
+ if ( rightIndex == -1 ) return false;
+ const XMP_Node * rightChild = rightNode.children[rightIndex];
+ if ( ! CompareSubtrees ( *leftChild, *rightChild ) ) return false;
+ }
+
+ } else {
+
+ // The parent must be simple or some other (not alt-text) kind of array.
+ XMP_Assert ( (! (leftNode.options & kXMP_PropCompositeMask)) || (leftNode.options & kXMP_PropValueIsArray) );
+ for ( size_t childNum = 0, childLim = leftNode.children.size(); childNum != childLim; ++childNum ) {
+ const XMP_Node * leftChild = leftNode.children[childNum];
+ const XMP_Node * rightChild = rightNode.children[childNum];
+ if ( ! CompareSubtrees ( *leftChild, *rightChild ) ) return false;
+ }
+
+ }
+
+ return true;
+
+} // CompareSubtrees
+
+// =================================================================================================
+// DeleteEmptySchema
+// =================
+
+void
+DeleteEmptySchema ( XMP_Node * schemaNode )
+{
+
+ if ( XMP_NodeIsSchema ( schemaNode->options ) && schemaNode->children.empty() ) {
+
+ XMP_Node * xmpTree = schemaNode->parent;
+
+ size_t schemaNum = 0;
+ size_t schemaLim = xmpTree->children.size();
+ while ( (schemaNum < schemaLim) && (xmpTree->children[schemaNum] != schemaNode) ) ++schemaNum;
+ XMP_Assert ( schemaNum < schemaLim );
+
+ XMP_NodePtrPos schemaPos = xmpTree->children.begin() + schemaNum;
+ XMP_Assert ( *schemaPos == schemaNode );
+
+ xmpTree->children.erase ( schemaPos );
+ delete schemaNode;
+
+ }
+
+} // DeleteEmptySchema
+
+// =================================================================================================
+// NormalizeLangValue
+// ==================
+//
+// Normalize an xml:lang value so that comparisons are effectively case insensitive as required by
+// RFC 3066 (which superceeds RFC 1766). The normalization rules:
+//
+// - The primary subtag is lower case, the suggested practice of ISO 639.
+// - All 2 letter secondary subtags are upper case, the suggested practice of ISO 3166.
+// - All other subtags are lower case.
+
+void
+NormalizeLangValue ( XMP_VarString * value )
+{
+ char * tagStart;
+ char * tagEnd;
+
+ // Find and process the primary subtag.
+
+ tagStart = (char*) value->c_str();
+ for ( tagEnd = tagStart; (*tagEnd != 0) && (*tagEnd != '-'); ++tagEnd ) {
+ if ( ('A' <= *tagEnd) && (*tagEnd <= 'Z') ) *tagEnd += 0x20;
+ }
+
+ // Find and process the secondary subtag.
+
+ tagStart = tagEnd;
+ if ( *tagStart == '-' ) ++tagStart;
+ for ( tagEnd = tagStart; (*tagEnd != 0) && (*tagEnd != '-'); ++tagEnd ) {
+ if ( ('A' <= *tagEnd) && (*tagEnd <= 'Z') ) *tagEnd += 0x20;
+ }
+ if ( tagEnd == tagStart+2 ) {
+ if ( ('a' <= *tagStart) && (*tagStart <= 'z') ) *tagStart -= 0x20;
+ ++tagStart;
+ if ( ('a' <= *tagStart) && (*tagStart <= 'z') ) *tagStart -= 0x20;
+ }
+
+ // Find and process the remaining subtags.
+
+ while ( true ) {
+ tagStart = tagEnd;
+ if ( *tagStart == '-' ) ++tagStart;
+ if ( *tagStart == 0 ) break;
+ for ( tagEnd = tagStart; (*tagEnd != 0) && (*tagEnd != '-'); ++tagEnd ) {
+ if ( ('A' <= *tagEnd) && (*tagEnd <= 'Z') ) *tagEnd += 0x20;
+ }
+ }
+
+} // NormalizeLangValue
+
+// =================================================================================================
+// NormalizeLangArray
+// ==================
+//
+// Make sure the x-default item is first. Touch up "single value" arrays that have a default plus
+// one real language. This case should have the same value for both items. Older Adobe apps were
+// hardwired to only use the 'x-default' item, so we copy that value to the other item.
+
+void
+NormalizeLangArray ( XMP_Node * array )
+{
+ XMP_Assert ( XMP_ArrayIsAltText(array->options) );
+
+ size_t itemNum;
+ size_t itemLim = array->children.size();
+ bool hasDefault = false;
+
+ for ( itemNum = 0; itemNum < itemLim; ++itemNum ) {
+
+ if ( array->children[itemNum]->qualifiers.empty() ||
+ (array->children[itemNum]->qualifiers[0]->name != "xml:lang") ) {
+ XMP_Throw ( "AltText array items must have an xml:lang qualifier", kXMPErr_BadXMP );
+ }
+
+ if ( array->children[itemNum]->qualifiers[0]->value == "x-default" ) {
+ hasDefault = true;
+ break;
+ }
+
+ }
+
+ if ( hasDefault ) {
+
+ if ( itemNum != 0 ) {
+ XMP_Node * temp = array->children[0];
+ array->children[0] = array->children[itemNum];
+ array->children[itemNum] = temp;
+ }
+
+ if ( itemLim == 2 ) array->children[1]->value = array->children[0]->value;
+
+ }
+
+} // NormalizeLangArray
+
+// =================================================================================================
+// DetectAltText
+// =============
+//
+// See if an array is an alt-text array. If so, make sure the x-default item is first.
+
+void
+DetectAltText ( XMP_Node * xmpParent )
+{
+ XMP_Assert ( XMP_ArrayIsAlternate(xmpParent->options) );
+
+ size_t itemNum, itemLim;
+
+ for ( itemNum = 0, itemLim = xmpParent->children.size(); itemNum < itemLim; ++itemNum ) {
+ XMP_OptionBits currOptions = xmpParent->children[itemNum]->options;
+ if ( (currOptions & kXMP_PropCompositeMask) || (! (currOptions & kXMP_PropHasLang)) ) break;
+ }
+
+ if ( (itemLim != 0) && (itemNum == itemLim) ) {
+ xmpParent->options |= kXMP_PropArrayIsAltText;
+ NormalizeLangArray ( xmpParent );
+ }
+
+} // DetectAltText
+
+// =================================================================================================
+// SortNamedNodes
+// ==============
+//
+// Sort the pointers in an XMP_NodeOffspring vector by name.
+
+static inline bool Compare ( const XMP_Node * left, const XMP_Node * right )
+{
+ return (left->name < right->name);
+}
+
+void
+SortNamedNodes ( XMP_NodeOffspring & nodeVector )
+{
+ sort ( nodeVector.begin(), nodeVector.end(), Compare );
+} // SortNamedNodes
+
+// =================================================================================================
diff --git a/gpr/source/lib/xmp_core/XMPCore_Impl.hpp b/gpr/source/lib/xmp_core/XMPCore_Impl.hpp
new file mode 100644
index 0000000..d900ef8
--- /dev/null
+++ b/gpr/source/lib/xmp_core/XMPCore_Impl.hpp
@@ -0,0 +1,392 @@
+#ifndef __XMPCore_Impl_hpp__
+#define __XMPCore_Impl_hpp__ 1
+
+// =================================================================================================
+// Copyright 2004 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.
+// =================================================================================================
+
+#include "public/include/XMP_Environment.h" // ! Must be the first #include!
+#include "public/include/XMP_Const.h"
+#include "XMP_BuildInfo.h"
+#include "XMP_LibUtils.hpp"
+
+// #include "XMPCore/source/XMPMeta.hpp"
+
+#include "public/include/client-glue/WXMP_Common.hpp"
+
+#include <vector>
+#include <string>
+#include <map>
+#include <cassert>
+#include <cstring>
+#include <cstdlib>
+
+#if XMP_WinBuild
+ #pragma warning ( disable : 4244 ) // possible loss of data (temporary for 64 bit builds)
+ #pragma warning ( disable : 4267 ) // possible loss of data (temporary for 64 bit builds)
+#endif
+
+// =================================================================================================
+// Primary internal types
+
+class XMP_Node;
+class XML_Node;
+class XPathStepInfo;
+
+typedef XMP_Node * XMP_NodePtr;
+
+typedef std::vector<XMP_Node*> XMP_NodeOffspring;
+typedef XMP_NodeOffspring::iterator XMP_NodePtrPos;
+
+typedef XMP_VarString::iterator XMP_VarStringPos;
+typedef XMP_VarString::const_iterator XMP_cVarStringPos;
+
+typedef std::vector < XPathStepInfo > XMP_ExpandedXPath;
+typedef XMP_ExpandedXPath::iterator XMP_ExpandedXPathPos;
+typedef XMP_ExpandedXPath::const_iterator XMP_cExpandedXPathPos;
+
+typedef std::map < XMP_VarString, XMP_ExpandedXPath > XMP_AliasMap; // Alias name to actual path.
+typedef XMP_AliasMap::iterator XMP_AliasMapPos;
+typedef XMP_AliasMap::const_iterator XMP_cAliasMapPos;
+
+// =================================================================================================
+// General global variables and macros
+
+extern XMP_Int32 sXMP_InitCount;
+
+extern XMP_NamespaceTable * sRegisteredNamespaces;
+
+extern XMP_AliasMap * sRegisteredAliasMap;
+
+#define WtoXMPMeta_Ref(xmpRef) (const XMPMeta &) (*((XMPMeta*)(xmpRef)))
+#define WtoXMPMeta_Ptr(xmpRef) ((XMPMeta*)(xmpRef))
+
+#define WtoXMPDocOps_Ptr(docRef) ((XMPDocOps*)(docRef))
+
+extern void * voidVoidPtr; // Used to backfill null output parameters.
+extern XMP_StringPtr voidStringPtr;
+extern XMP_StringLen voidStringLen;
+extern XMP_OptionBits voidOptionBits;
+extern XMP_Bool voidByte;
+extern bool voidBool;
+extern XMP_Int32 voidInt32;
+extern XMP_Int64 voidInt64;
+extern double voidDouble;
+extern XMP_DateTime voidDateTime;
+extern WXMP_Result void_wResult;
+
+#define kHexDigits "0123456789ABCDEF"
+
+#define XMP_LitMatch(s,l) (strcmp((s),(l)) == 0)
+#define XMP_LitNMatch(s,l,n) (strncmp((s),(l),(n)) == 0)
+ // *** Use the above macros!
+
+#if XMP_WinBuild
+ #define snprintf _snprintf
+#endif
+
+// =================================================================================================
+// Version info
+
+#if XMP_DebugBuild
+ #define kXMPCore_DebugFlag 1
+#else
+ #define kXMPCore_DebugFlag 0
+#endif
+
+#define kXMPCore_VersionNumber ( (kXMPCore_DebugFlag << 31) | \
+ (XMP_API_VERSION_MAJOR << 24) | \
+ (XMP_API_VERSION_MINOR << 16) | \
+ (XMP_API_VERSION_MICRO << 8) )
+
+ #define kXMPCoreName "XMP Core"
+ #define kXMPCore_VersionMessage kXMPCoreName " " XMPCORE_API_VERSION_STRING
+// =================================================================================================
+// Support for call tracing
+
+#ifndef XMP_TraceCoreCalls
+ #define XMP_TraceCoreCalls 0
+ #define XMP_TraceCoreCallsToFile 0
+#endif
+
+#if XMP_TraceCoreCalls
+
+ #undef AnnounceThrow
+ #undef AnnounceCatch
+
+ #undef AnnounceEntry
+ #undef AnnounceNoLock
+ #undef AnnounceExit
+
+ extern FILE * xmpCoreLog;
+
+ #define AnnounceThrow(msg) \
+ fprintf ( xmpCoreLog, "XMP_Throw: %s\n", msg ); fflush ( xmpCoreLog )
+ #define AnnounceCatch(msg) \
+ fprintf ( xmpCoreLog, "Catch in %s: %s\n", procName, msg ); fflush ( xmpCoreLog )
+
+ #define AnnounceEntry(proc) \
+ const char * procName = proc; \
+ fprintf ( xmpCoreLog, "Entering %s\n", procName ); fflush ( xmpCoreLog )
+ #define AnnounceNoLock(proc) \
+ const char * procName = proc; \
+ fprintf ( xmpCoreLog, "Entering %s (no lock)\n", procName ); fflush ( xmpCoreLog )
+ #define AnnounceExit() \
+ fprintf ( xmpCoreLog, "Exiting %s\n", procName ); fflush ( xmpCoreLog )
+
+#endif
+
+// =================================================================================================
+// ExpandXPath, FindNode, and related support
+
+// *** Normalize the use of "const xx &" for input params
+
+#define kXMP_ArrayItemName "[]"
+
+#define kXMP_CreateNodes true
+#define kXMP_ExistingOnly false
+
+#define FindConstSchema(t,u) FindSchemaNode ( const_cast<XMP_Node*>(t), u, kXMP_ExistingOnly, 0 )
+#define FindConstChild(p,c) FindChildNode ( const_cast<XMP_Node*>(p), c, kXMP_ExistingOnly, 0 )
+#define FindConstQualifier(p,c) FindQualifierNode ( const_cast<XMP_Node*>(p), c, kXMP_ExistingOnly, 0 )
+#define FindConstNode(t,p) FindNode ( const_cast<XMP_Node*>(t), p, kXMP_ExistingOnly, 0 )
+
+extern XMP_OptionBits
+VerifySetOptions ( XMP_OptionBits options, XMP_StringPtr propValue );
+
+extern void
+ComposeXPath ( const XMP_ExpandedXPath & expandedXPath,
+ XMP_VarString * stringXPath );
+
+extern void
+ExpandXPath ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propPath,
+ XMP_ExpandedXPath * expandedXPath );
+
+extern XMP_Node *
+FindSchemaNode ( XMP_Node * xmpTree,
+ XMP_StringPtr nsURI,
+ bool createNodes,
+ XMP_NodePtrPos * ptrPos = 0 );
+
+extern XMP_Node *
+FindChildNode ( XMP_Node * parent,
+ XMP_StringPtr childName,
+ bool createNodes,
+ XMP_NodePtrPos * ptrPos = 0 );
+
+extern XMP_Node *
+FindQualifierNode ( XMP_Node * parent,
+ XMP_StringPtr qualName,
+ bool createNodes,
+ XMP_NodePtrPos * ptrPos = 0 );
+
+extern XMP_Node *
+FindNode ( XMP_Node * xmpTree,
+ const XMP_ExpandedXPath & expandedXPath,
+ bool createNodes,
+ XMP_OptionBits leafOptions = 0,
+ XMP_NodePtrPos * ptrPos = 0 );
+
+extern XMP_Index
+LookupLangItem ( const XMP_Node * arrayNode, XMP_VarString & lang ); // ! Lang must be normalized!
+
+extern XMP_Index
+LookupFieldSelector ( const XMP_Node * arrayNode, XMP_StringPtr fieldName, XMP_StringPtr fieldValue );
+
+extern void
+CloneOffspring ( const XMP_Node * origParent, XMP_Node * cloneParent, bool skipEmpty = false );
+
+extern XMP_Node *
+CloneSubtree ( const XMP_Node * origRoot, XMP_Node * cloneParent, bool skipEmpty = false );
+
+extern bool
+CompareSubtrees ( const XMP_Node & leftNode, const XMP_Node & rightNode );
+
+extern void
+DeleteSubtree ( XMP_NodePtrPos rootNodePos );
+
+extern void
+DeleteEmptySchema ( XMP_Node * schemaNode );
+
+extern void
+NormalizeLangValue ( XMP_VarString * value );
+
+extern void
+NormalizeLangArray ( XMP_Node * array );
+
+extern void
+DetectAltText ( XMP_Node * xmpParent );
+
+extern void
+SortNamedNodes ( XMP_NodeOffspring & nodeVector );
+
+static inline bool
+IsPathPrefix ( XMP_StringPtr fullPath, XMP_StringPtr prefix )
+{
+ bool isPrefix = false;
+ XMP_StringLen prefixLen = strlen(prefix);
+ if ( XMP_LitNMatch ( prefix, fullPath, prefixLen ) ) {
+ char separator = fullPath[prefixLen];
+ if ( (separator == 0) || (separator == '/') ||
+ (separator == '[') || (separator == '*') ) isPrefix = true;
+ }
+ return isPrefix;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+class XPathStepInfo {
+public:
+ XMP_VarString step;
+ XMP_OptionBits options;
+ XPathStepInfo ( XMP_StringPtr _step, XMP_OptionBits _options ) : step(_step), options(_options) {};
+ XPathStepInfo ( XMP_VarString _step, XMP_OptionBits _options ) : step(_step), options(_options) {};
+private:
+ XPathStepInfo() : options(0) {}; // ! Hide the default constructor.
+};
+
+enum { kSchemaStep = 0, kRootPropStep = 1, kAliasIndexStep = 2 };
+
+enum { // Bits for XPathStepInfo options. // *** Add mask check to init code.
+ kXMP_StepKindMask = 0x0F, // ! The step kinds are mutually exclusive numbers.
+ kXMP_StructFieldStep = 0x01, // Also for top level nodes (schema "fields").
+ kXMP_QualifierStep = 0x02, // ! Order is significant to separate struct/qual from array kinds!
+ kXMP_ArrayIndexStep = 0x03, // ! The kinds must not overlay array form bits!
+ kXMP_ArrayLastStep = 0x04,
+ kXMP_QualSelectorStep = 0x05,
+ kXMP_FieldSelectorStep = 0x06,
+ kXMP_StepIsAlias = 0x10
+};
+
+#define GetStepKind(f) ((f) & kXMP_StepKindMask)
+
+#define kXMP_NewImplicitNode kXMP_InsertAfterItem
+
+// =================================================================================================
+// XMP_Node details
+
+#if 0 // Pattern for iterating over the children or qualifiers:
+ for ( size_t xxNum = 0, xxLim = _node_->_offspring_.size(); xxNum < xxLim; ++xxNum ) {
+ const XMP_Node * _curr_ = _node_->_offspring_[xxNum];
+ }
+#endif
+
+class XMP_Node {
+public:
+
+ XMP_OptionBits options;
+ XMP_VarString name, value;
+ XMP_Node * parent;
+ XMP_NodeOffspring children;
+ XMP_NodeOffspring qualifiers;
+ #if XMP_DebugBuild
+ // *** XMP_StringPtr _namePtr, _valuePtr; // *** Not working, need operator=?
+ #endif
+
+ XMP_Node ( XMP_Node * _parent, XMP_StringPtr _name, XMP_OptionBits _options )
+ : options(_options), name(_name), parent(_parent)
+ {
+ #if XMP_DebugBuild
+ XMP_Assert ( (name.find ( ':' ) != XMP_VarString::npos) || (name == kXMP_ArrayItemName) ||
+ (options & kXMP_SchemaNode) || (parent == 0) );
+ // *** _namePtr = name.c_str();
+ // *** _valuePtr = value.c_str();
+ #endif
+ };
+
+ XMP_Node ( XMP_Node * _parent, const XMP_VarString & _name, XMP_OptionBits _options )
+ : options(_options), name(_name), parent(_parent)
+ {
+ #if XMP_DebugBuild
+ XMP_Assert ( (name.find ( ':' ) != XMP_VarString::npos) || (name == kXMP_ArrayItemName) ||
+ (options & kXMP_SchemaNode) || (parent == 0) );
+ // *** _namePtr = name.c_str();
+ // *** _valuePtr = value.c_str();
+ #endif
+ };
+
+ XMP_Node ( XMP_Node * _parent, XMP_StringPtr _name, XMP_StringPtr _value, XMP_OptionBits _options )
+ : options(_options), name(_name), value(_value), parent(_parent)
+ {
+ #if XMP_DebugBuild
+ XMP_Assert ( (name.find ( ':' ) != XMP_VarString::npos) || (name == kXMP_ArrayItemName) ||
+ (options & kXMP_SchemaNode) || (parent == 0) );
+ // *** _namePtr = name.c_str();
+ // *** _valuePtr = value.c_str();
+ #endif
+ };
+
+ XMP_Node ( XMP_Node * _parent, const XMP_VarString & _name, const XMP_VarString & _value, XMP_OptionBits _options )
+ : options(_options), name(_name), value(_value), parent(_parent)
+ {
+ #if XMP_DebugBuild
+ XMP_Assert ( (name.find ( ':' ) != XMP_VarString::npos) || (name == kXMP_ArrayItemName) ||
+ (options & kXMP_SchemaNode) || (parent == 0) );
+ // *** _namePtr = name.c_str();
+ // *** _valuePtr = value.c_str();
+ #endif
+ };
+
+ void GetLocalURI ( XMP_StringPtr * uriStr, XMP_StringLen * uriSize ) const;
+
+ void RemoveChildren()
+ {
+ for ( size_t i = 0, vLim = children.size(); i < vLim; ++i ) {
+ if ( children[i] != 0 ) delete children[i];
+ }
+ children.clear();
+ }
+
+ void RemoveQualifiers()
+ {
+ for ( size_t i = 0, vLim = qualifiers.size(); i < vLim; ++i ) {
+ if ( qualifiers[i] != 0 ) delete qualifiers[i];
+ }
+ qualifiers.clear();
+ }
+
+ void ClearNode()
+ {
+ options = 0;
+ name.erase();
+ value.erase();
+ this->RemoveChildren();
+ this->RemoveQualifiers();
+ }
+
+ virtual ~XMP_Node() { RemoveChildren(); RemoveQualifiers(); };
+
+private:
+ XMP_Node() : options(0), parent(0) // ! Make sure parent pointer is always set.
+ {
+ #if XMP_DebugBuild
+ // *** _namePtr = name.c_str();
+ // *** _valuePtr = value.c_str();
+ #endif
+ };
+
+};
+
+class XMP_AutoNode { // Used to hold a child during subtree construction.
+public:
+ XMP_Node * nodePtr;
+ XMP_AutoNode() : nodePtr(0) {};
+ ~XMP_AutoNode() { if ( nodePtr != 0 ) delete ( nodePtr ); nodePtr = 0; };
+ XMP_AutoNode ( XMP_Node * _parent, XMP_StringPtr _name, XMP_OptionBits _options )
+ : nodePtr ( new XMP_Node ( _parent, _name, _options ) ) {};
+ XMP_AutoNode ( XMP_Node * _parent, const XMP_VarString & _name, XMP_OptionBits _options )
+ : nodePtr ( new XMP_Node ( _parent, _name, _options ) ) {};
+ XMP_AutoNode ( XMP_Node * _parent, XMP_StringPtr _name, XMP_StringPtr _value, XMP_OptionBits _options )
+ : nodePtr ( new XMP_Node ( _parent, _name, _value, _options ) ) {};
+ XMP_AutoNode ( XMP_Node * _parent, const XMP_VarString & _name, const XMP_VarString & _value, XMP_OptionBits _options )
+ : nodePtr ( new XMP_Node ( _parent, _name, _value, _options ) ) {};
+};
+
+// =================================================================================================
+
+#endif // __XMPCore_Impl_hpp__
diff --git a/gpr/source/lib/xmp_core/XMPIterator.cpp b/gpr/source/lib/xmp_core/XMPIterator.cpp
new file mode 100644
index 0000000..229cd8f
--- /dev/null
+++ b/gpr/source/lib/xmp_core/XMPIterator.cpp
@@ -0,0 +1,637 @@
+// =================================================================================================
+// Copyright 2003 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.
+// =================================================================================================
+
+#include "public/include/XMP_Environment.h" // ! This must be the first include!
+#include "XMPCore_Impl.hpp"
+
+#include "XMPIterator.hpp"
+
+#include <string>
+#include <stdio.h> // For snprintf.
+
+#if XMP_WinBuild
+ #pragma warning ( disable : 4702 ) // unreachable code
+ #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning)
+ #pragma warning ( disable : 4996 ) // '...' was declared deprecated
+#endif
+
+// =================================================================================================
+// Support Routines
+// =================================================================================================
+
+
+#ifndef TraceIterators
+ #define TraceIterators 0
+#endif
+
+#if TraceIterators
+ static const char * sStageNames[] = { "before", "self", "qualifiers", "children" };
+#endif
+
+static XMP_Node * sDummySchema = 0; // ! Used for some ugliness with aliases.
+
+// -------------------------------------------------------------------------------------------------
+// AddSchemaProps
+// --------------
+//
+// Add the top level properties to the IterNode for a schema.
+
+static void
+AddSchemaProps ( IterInfo & info, IterNode & iterSchema, const XMP_Node * xmpSchema )
+{
+ #if TraceIterators
+ printf ( " Adding properties of %s\n", xmpSchema->name.c_str() );
+ #endif
+
+ for ( size_t propNum = 0, propLim = xmpSchema->children.size(); propNum != propLim; ++propNum ) {
+ const XMP_Node * xmpProp = xmpSchema->children[propNum];
+ // *** set the has-aliases bit when appropriate
+ iterSchema.children.push_back ( IterNode ( xmpProp->options, xmpProp->name, 0 ) );
+ #if TraceIterators
+ printf ( " %s\n", xmpProp->name.c_str() );
+ #endif
+ }
+
+} // AddSchemaProps
+
+// -------------------------------------------------------------------------------------------------
+// AddNodeOffspring
+// ----------------
+//
+// Add the immediate children and qualifiers to an IterNode.
+
+static void
+AddNodeOffspring ( IterInfo & info, IterNode & iterParent, const XMP_Node * xmpParent )
+{
+ XMP_VarString currPath ( iterParent.fullPath );
+ size_t leafOffset = iterParent.fullPath.size();
+
+ if ( (! xmpParent->qualifiers.empty()) && (! (info.options & kXMP_IterOmitQualifiers)) ) {
+
+ #if TraceIterators
+ printf ( " Adding qualifiers of %s\n", currPath.c_str() );
+ #endif
+
+ currPath += "/?"; // All qualifiers are named and use paths like "Prop/?Qual".
+ leafOffset += 2;
+
+ for ( size_t qualNum = 0, qualLim = xmpParent->qualifiers.size(); qualNum != qualLim; ++qualNum ) {
+ const XMP_Node * xmpQual = xmpParent->qualifiers[qualNum];
+ currPath += xmpQual->name;
+ iterParent.qualifiers.push_back ( IterNode ( xmpQual->options, currPath, leafOffset ) );
+ currPath.erase ( leafOffset );
+ #if TraceIterators
+ printf ( " %s\n", xmpQual->name.c_str() );
+ #endif
+ }
+
+ leafOffset -= 2;
+ currPath.erase ( leafOffset );
+
+ }
+
+ if ( ! xmpParent->children.empty() ) {
+
+ #if TraceIterators
+ printf ( " Adding children of %s\n", currPath.c_str() );
+ #endif
+
+ XMP_Assert ( xmpParent->options & kXMP_PropCompositeMask );
+
+ if ( xmpParent->options & kXMP_PropValueIsStruct ) {
+ currPath += '/';
+ leafOffset += 1;
+ }
+
+ for ( size_t childNum = 0, childLim = xmpParent->children.size(); childNum != childLim; ++childNum ) {
+ const XMP_Node * xmpChild = xmpParent->children[childNum];
+ if ( ! (xmpParent->options & kXMP_PropValueIsArray) ) {
+ currPath += xmpChild->name;
+ } else {
+ char buffer [32]; // AUDIT: Using sizeof(buffer) below for snprintf length is safe.
+ snprintf ( buffer, sizeof(buffer), "[%lu]", childNum+1 ); // ! XPath indices are one-based.
+ currPath += buffer;
+ }
+ iterParent.children.push_back ( IterNode ( xmpChild->options, currPath, leafOffset ) );
+ currPath.erase ( leafOffset );
+ #if TraceIterators
+ printf ( " %s\n", (iterParent.children.back().fullPath.c_str() + leafOffset) );
+ #endif
+ }
+
+ }
+
+} // AddNodeOffspring
+
+// -------------------------------------------------------------------------------------------------
+// SetCurrSchema
+// -------------
+
+static inline void
+SetCurrSchema ( IterInfo & info, XMP_StringPtr schemaName )
+{
+
+ info.currSchema = schemaName;
+ #if 0 // *** XMP_DebugBuild
+ info._schemaPtr = info.currSchema.c_str();
+ #endif
+
+} // SetCurrSchema
+
+static inline void
+SetCurrSchema ( IterInfo & info, XMP_VarString & schemaName )
+{
+
+ info.currSchema = schemaName;
+ #if 0 // *** XMP_DebugBuild
+ info._schemaPtr = info.currSchema.c_str();
+ #endif
+
+} // SetCurrSchema
+
+// -------------------------------------------------------------------------------------------------
+// AdvanceIterPos
+// --------------
+//
+// Adjust currPos and possibly endPos for the next step in a pre-order depth-first traversal. The
+// current node has just been visited, move on to its qualifiers, children, then siblings, or back
+// up to an ancestor. AdvanceIterPos either moves to a property or qualifier node that can be
+// visited, or to the end of the entire iteration.
+
+static void
+AdvanceIterPos ( IterInfo & info )
+{
+ // -------------------------------------------------------------------------------------------
+ // Keep looking until we find a node to visit or the end of everything. The first time through
+ // the current node will exist, we just visited it. But we have to keep looking if the current
+ // node was the last of its siblings or is an empty schema.
+
+ // ! It is possible that info.currPos == info.endPos on entry. Don't dereference info.currPos yet!
+
+ while ( true ) {
+
+ if ( info.currPos == info.endPos ) {
+
+ // ------------------------------------------------------------------------------------
+ // At the end of a set of siblings, move up to an ancestor. We've either just finished
+ // the qualifiers and will move to the children, or have just finished the children and
+ // will move on to the next sibling.
+
+ if ( info.ancestors.empty() ) break; // We're at the end of the schema list.
+
+ IterPosPair & parent = info.ancestors.back();
+ info.currPos = parent.first;
+ info.endPos = parent.second;
+ info.ancestors.pop_back();
+
+ #if TraceIterators
+ printf ( " Moved up to %s, stage = %s\n",
+ info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage] );
+ #endif
+
+ } else {
+
+ // -------------------------------------------------------------------------------------------
+ // Decide what to do with this iteration node based on its state. Don't use a switch statment,
+ // some of the cases want to break from the loop. A break in a switch just exits the case.
+
+ #if TraceIterators
+ printf ( " Moving from %s, stage = %s\n",
+ info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage] );
+ #endif
+
+ if ( info.currPos->visitStage == kIter_BeforeVisit ) { // Visit this node now.
+ if ( info.currPos->options & kXMP_SchemaNode ) SetCurrSchema ( info, info.currPos->fullPath );
+ break;
+ }
+
+ if ( info.currPos->visitStage == kIter_VisitSelf ) { // Just finished visiting the value portion.
+ info.currPos->visitStage = kIter_VisitQualifiers; // Start visiting the qualifiers.
+ if ( ! info.currPos->qualifiers.empty() ) {
+ info.ancestors.push_back ( IterPosPair ( info.currPos, info.endPos ) );
+ info.endPos = info.currPos->qualifiers.end(); // ! Set the parent's endPos before changing currPos!
+ info.currPos = info.currPos->qualifiers.begin();
+ break;
+ }
+ }
+
+ if ( info.currPos->visitStage == kIter_VisitQualifiers ) { // Just finished visiting the qualifiers.
+ info.currPos->qualifiers.clear();
+ info.currPos->visitStage = kIter_VisitChildren; // Start visiting the children.
+ if ( ! info.currPos->children.empty() ) {
+ info.ancestors.push_back ( IterPosPair ( info.currPos, info.endPos ) );
+ info.endPos = info.currPos->children.end(); // ! Set the parent's endPos before changing currPos!
+ info.currPos = info.currPos->children.begin();
+ break;
+ }
+ }
+
+ if ( info.currPos->visitStage == kIter_VisitChildren ) { // Just finished visiting the children.
+ info.currPos->children.clear();
+ ++info.currPos; // Move to the next sibling.
+ continue;
+ }
+
+ #if TraceIterators
+ if ( info.currPos != info.endPos ) {
+ printf ( " Moved to %s, stage = %s\n",
+ info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage] );
+ }
+ #endif
+
+ }
+
+ } // Loop to find the next node.
+
+ XMP_Assert ( (info.currPos == info.endPos) || (info.currPos->visitStage == kIter_BeforeVisit) );
+
+} // AdvanceIterPos
+
+// -------------------------------------------------------------------------------------------------
+// GetNextXMPNode
+// --------------
+//
+// Used by XMPIterator::Next to obtain the next XMP node, ignoring the kXMP_IterJustLeafNodes flag.
+// This isolates some messy code, allowing a clean loop in Next if kXMP_IterJustLeafNodes is set.
+
+static const XMP_Node *
+GetNextXMPNode ( IterInfo & info )
+{
+ const XMP_Node * xmpNode = 0;
+
+ // ----------------------------------------------------------------------------------------------
+ // On entry currPos points to an iteration node whose state is either before-visit or visit-self.
+ // If it is before-visit then we will return that node's value part now. If it is visit-self it
+ // means the previous iteration returned the value portion of that node, so we can advance to the
+ // next node in the iteration tree. Then we find the corresponding XMP node, allowing for the XMP
+ // tree to have been modified since that part of the iteration tree was constructed.
+
+ // ! NOTE: Supporting aliases throws in some nastiness with schemas. There might not be any XMP
+ // ! node for the schema, but we still have to visit it because of possible aliases. The static
+ // ! sDummySchema is returned if there is no real schema node.
+
+ if ( info.currPos->visitStage != kIter_BeforeVisit ) AdvanceIterPos ( info );
+
+ bool isSchemaNode = false;
+ XMP_ExpandedXPath expPath; // Keep outside the loop to avoid constant construct/destruct.
+
+ while ( info.currPos != info.endPos ) {
+
+ isSchemaNode = XMP_NodeIsSchema ( info.currPos->options );
+ if ( isSchemaNode ) {
+ SetCurrSchema ( info, info.currPos->fullPath );
+ xmpNode = FindConstSchema ( &info.xmpObj->tree, info.currPos->fullPath.c_str() );
+ if ( xmpNode == 0 ) xmpNode = sDummySchema;
+ } else {
+ ExpandXPath ( info.currSchema.c_str(), info.currPos->fullPath.c_str(), &expPath );
+ xmpNode = FindConstNode ( &info.xmpObj->tree, expPath );
+ }
+ if ( xmpNode != 0 ) break; // Exit the loop, we found a live XMP node.
+
+ info.currPos->visitStage = kIter_VisitChildren; // Make AdvanceIterPos move to the next sibling.
+ info.currPos->children.clear();
+ info.currPos->qualifiers.clear();
+ AdvanceIterPos ( info );
+
+ }
+
+ if ( info.currPos == info.endPos ) return 0;
+
+ // -------------------------------------------------------------------------------------------
+ // Now we've got the iteration node and corresponding XMP node. Add the iteration children for
+ // structs and arrays. The children of schema were added when the iterator was constructed.
+
+ XMP_Assert ( info.currPos->visitStage == kIter_BeforeVisit );
+
+ if ( info.currPos->visitStage == kIter_BeforeVisit ) {
+ if ( (! isSchemaNode) && (! (info.options & kXMP_IterJustChildren)) ) {
+ AddNodeOffspring ( info, *info.currPos, xmpNode );
+ }
+ info.currPos->visitStage = kIter_VisitSelf;
+ }
+
+ return xmpNode;
+
+} // GetNextXMPNode
+
+// =================================================================================================
+// Init/Term
+// =================================================================================================
+
+// -------------------------------------------------------------------------------------------------
+// Initialize
+// ----------
+
+/* class static */ bool
+XMPIterator::Initialize()
+{
+ sDummySchema = new XMP_Node ( 0, "dummy:schema/", kXMP_SchemaNode);
+ return true;
+
+} // Initialize
+
+// -------------------------------------------------------------------------------------------------
+// Terminate
+// ----------
+
+/* class static */ void
+XMPIterator::Terminate() RELEASE_NO_THROW
+{
+ delete ( sDummySchema );
+ sDummySchema = 0;
+ return;
+
+} // Terminate
+
+// =================================================================================================
+// Constructors
+// =================================================================================================
+
+// -------------------------------------------------------------------------------------------------
+// XMPIterator
+// -----------
+//
+// Constructor for iterations over the nodes in an XMPMeta object. This builds a tree of iteration
+// nodes that caches the existing node names of the XMPMeta object. The iteration tree is a partial
+// replica of the XMPMeta tree. The initial iteration tree normally has just the root node, all of
+// the schema nodes for a full object iteration. Lower level nodes (children and qualifiers) are
+// added when the parent is visited. If the kXMP_IterJustChildren option is passed then the initial
+// iterator includes the children and the parent is marked as done. The iteration tree nodes are
+// pruned when they are no longer needed.
+
+XMPIterator::XMPIterator ( const XMPMeta & xmpObj,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_OptionBits options ) : info(IterInfo(options,&xmpObj)), clientRefs(0)
+{
+ if ( (options & kXMP_IterClassMask) != kXMP_IterProperties ) {
+ XMP_Throw ( "Unsupported iteration kind", kXMPErr_BadOptions );
+ }
+
+ // *** Lock the XMPMeta object if we ever stop using a full DLL lock.
+
+ if ( *propName != 0 ) {
+
+ // An iterator rooted at a specific node.
+
+ #if TraceIterators
+ printf ( "\nNew XMP property iterator for \"%s\", options = %X\n Schema = %s, root = %s\n",
+ xmpObj.tree.name.c_str(), options, schemaNS, propName );
+ #endif
+
+ XMP_ExpandedXPath propPath;
+ ExpandXPath ( schemaNS, propName, &propPath );
+ XMP_Node * propNode = FindConstNode ( &xmpObj.tree, propPath ); // If not found get empty iteration.
+
+ if ( propNode != 0 ) {
+
+ XMP_VarString rootName ( propPath[1].step ); // The schema is [0].
+ for ( size_t i = 2; i < propPath.size(); ++i ) {
+ XMP_OptionBits stepKind = GetStepKind ( propPath[i].options );
+ if ( stepKind <= kXMP_QualifierStep ) rootName += '/';
+ rootName += propPath[i].step;
+ }
+
+ propName = rootName.c_str();
+ size_t leafOffset = rootName.size();
+ while ( (leafOffset > 0) && (propName[leafOffset] != '/') && (propName[leafOffset] != '[') ) --leafOffset;
+ if ( propName[leafOffset] == '/' ) ++leafOffset;
+
+ info.tree.children.push_back ( IterNode ( propNode->options, propName, leafOffset ) );
+ SetCurrSchema ( info, propPath[kSchemaStep].step.c_str() );
+ if ( info.options & kXMP_IterJustChildren ) {
+ AddNodeOffspring ( info, info.tree.children.back(), propNode );
+ }
+
+ }
+
+ } else if ( *schemaNS != 0 ) {
+
+ // An iterator for all properties in one schema.
+
+ #if TraceIterators
+ printf ( "\nNew XMP schema iterator for \"%s\", options = %X\n Schema = %s\n",
+ xmpObj.tree.name.c_str(), options, schemaNS );
+ #endif
+
+ info.tree.children.push_back ( IterNode ( kXMP_SchemaNode, schemaNS, 0 ) );
+ IterNode & iterSchema = info.tree.children.back();
+
+ XMP_Node * xmpSchema = FindConstSchema ( &xmpObj.tree, schemaNS );
+ if ( xmpSchema != 0 ) AddSchemaProps ( info, iterSchema, xmpSchema );
+
+ if ( iterSchema.children.empty() ) {
+ info.tree.children.pop_back(); // No properties, remove the schema node.
+ } else {
+ SetCurrSchema ( info, schemaNS );
+ }
+
+ } else {
+
+ // An iterator for all properties in all schema. First add schema that exist (have children),
+ // adding aliases from them if appropriate. Then add schema that have no actual properties
+ // but do have aliases to existing properties, if we're including aliases in the iteration.
+
+ #if TraceIterators
+ printf ( "\nNew XMP tree iterator for \"%s\", options = %X\n",
+ xmpObj.tree.name.c_str(), options );
+ #endif
+
+ // First pick up the schema that exist.
+
+ for ( size_t schemaNum = 0, schemaLim = xmpObj.tree.children.size(); schemaNum != schemaLim; ++schemaNum ) {
+
+ const XMP_Node * xmpSchema = xmpObj.tree.children[schemaNum];
+ info.tree.children.push_back ( IterNode ( kXMP_SchemaNode, xmpSchema->name, 0 ) );
+ IterNode & iterSchema = info.tree.children.back();
+
+ if ( ! (info.options & kXMP_IterJustChildren) ) {
+ AddSchemaProps ( info, iterSchema, xmpSchema );
+ if ( iterSchema.children.empty() ) info.tree.children.pop_back(); // No properties, remove the schema node.
+ }
+
+ }
+
+ }
+
+ // Set the current iteration position to the first node to be visited.
+
+ info.currPos = info.tree.children.begin();
+ info.endPos = info.tree.children.end();
+
+ if ( (info.options & kXMP_IterJustChildren) && (info.currPos != info.endPos) && (*schemaNS != 0) ) {
+ info.currPos->visitStage = kIter_VisitSelf;
+ }
+
+ #if TraceIterators
+ if ( info.currPos == info.endPos ) {
+ printf ( " ** Empty iteration **\n" );
+ } else {
+ printf ( " Initial node %s, stage = %s, iterator @ %.8X\n",
+ info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage], this );
+ }
+ #endif
+
+} // XMPIterator for XMPMeta objects
+
+// -------------------------------------------------------------------------------------------------
+// XMPIterator
+// -----------
+//
+// Constructor for iterations over global tables such as registered namespaces or aliases.
+
+XMPIterator::XMPIterator ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_OptionBits options ) : info(IterInfo(options,0)), clientRefs(0)
+{
+
+ XMP_Throw ( "Unimplemented XMPIterator constructor for global tables", kXMPErr_Unimplemented );
+ void * p; p = &schemaNS; p = &propName; p = &options; // Avoid unused param warnings.
+
+} // XMPIterator for global tables
+
+// -------------------------------------------------------------------------------------------------
+// ~XMPIterator
+// -----------
+
+XMPIterator::~XMPIterator() RELEASE_NO_THROW
+{
+ XMP_Assert ( this->clientRefs <= 0 );
+ // Let everything else default.
+
+} // ~XMPIterator
+
+// =================================================================================================
+// Iteration Methods
+// =================================================================================================
+
+// -------------------------------------------------------------------------------------------------
+// Next
+// ----
+//
+// Do a preorder traversal of the cached nodes.
+
+// *** Need to document the relationships between currPos, endPos, and visitStage.
+
+bool
+XMPIterator::Next ( XMP_StringPtr * schemaNS,
+ XMP_StringLen * nsSize,
+ XMP_StringPtr * propPath,
+ XMP_StringLen * pathSize,
+ XMP_StringPtr * propValue,
+ XMP_StringLen * valueSize,
+ XMP_OptionBits * propOptions )
+{
+ // *** Lock the XMPMeta object if we ever stop using a full DLL lock.
+
+ // ! NOTE: Supporting aliases throws in some nastiness with schemas. There might not be any XMP
+ // ! node for the schema, but we still have to visit it because of possible aliases.
+
+ if ( info.currPos == info.endPos ) return false; // Happens at the start of an empty iteration.
+
+ #if TraceIterators
+ printf ( "Next iteration from %s, stage = %s, iterator @ %.8X\n",
+ info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage], this );
+ #endif
+
+ const XMP_Node * xmpNode = GetNextXMPNode ( info );
+ if ( xmpNode == 0 ) return false;
+ bool isSchemaNode = XMP_NodeIsSchema ( info.currPos->options );
+
+ if ( info.options & kXMP_IterJustLeafNodes ) {
+ while ( isSchemaNode || (! xmpNode->children.empty()) ) {
+ info.currPos->visitStage = kIter_VisitQualifiers; // Skip to this node's children.
+ xmpNode = GetNextXMPNode ( info );
+ if ( xmpNode == 0 ) return false;
+ isSchemaNode = XMP_NodeIsSchema ( info.currPos->options );
+ }
+ }
+
+ *schemaNS = info.currSchema.c_str();
+ *nsSize = info.currSchema.size();
+
+ *propOptions = info.currPos->options;
+
+ *propPath = "";
+ *pathSize = 0;
+ *propValue = "";
+ *valueSize = 0;
+
+ if ( ! (*propOptions & kXMP_SchemaNode) ) {
+
+ *propPath = info.currPos->fullPath.c_str();
+ *pathSize = info.currPos->fullPath.size();
+
+ if ( info.options & kXMP_IterJustLeafName ) {
+ *propPath += info.currPos->leafOffset;
+ *pathSize -= info.currPos->leafOffset;
+ xmpNode->GetLocalURI ( schemaNS, nsSize ); // Use the leaf namespace, not the top namespace.
+ }
+
+ if ( ! (*propOptions & kXMP_PropCompositeMask) ) {
+ *propValue = xmpNode->value.c_str();
+ *valueSize = xmpNode->value.size();
+ }
+
+ }
+
+ #if TraceIterators
+ printf ( " Next node %s, stage = %s\n",
+ info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage] );
+ #endif
+
+ return true;
+
+} // Next
+
+// -------------------------------------------------------------------------------------------------
+// Skip
+// ----
+//
+// Skip some portion of the traversal related to the last visited node. We skip either that node's
+// children, or those children and the previous node's siblings. The implementation might look a bit
+// awkward because info.currNode always points to the next node to be visited. We might already have
+// moved past the things to skip, e.g. if the previous node was simple and the last of its siblings.
+
+enum {
+ kXMP_ValidIterSkipOptions = kXMP_IterSkipSubtree | kXMP_IterSkipSiblings
+};
+
+void
+XMPIterator::Skip ( XMP_OptionBits iterOptions )
+{
+// if ( (info.currPos == kIter_NullPos) ) XMP_Throw ( "No prior postion to skip from", kXMPErr_BadIterPosition );
+ if ( iterOptions == 0 ) XMP_Throw ( "Must specify what to skip", kXMPErr_BadOptions );
+ if ( (iterOptions & ~kXMP_ValidIterSkipOptions) != 0 ) XMP_Throw ( "Undefined options", kXMPErr_BadOptions );
+
+ #if TraceIterators
+ printf ( "Skipping from %s, stage = %s, iterator @ %.8X",
+ info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage], this );
+ #endif
+
+ if ( iterOptions & kXMP_IterSkipSubtree ) {
+ #if TraceIterators
+ printf ( ", mode = subtree\n" );
+ #endif
+ info.currPos->visitStage = kIter_VisitChildren;
+ } else if ( iterOptions & kXMP_IterSkipSiblings ) {
+ #if TraceIterators
+ printf ( ", mode = siblings\n" );
+ #endif
+ info.currPos = info.endPos;
+ AdvanceIterPos ( info );
+ }
+ #if TraceIterators
+ printf ( " Skipped to %s, stage = %s\n",
+ info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage] );
+ #endif
+
+
+} // Skip
+
+// =================================================================================================
diff --git a/gpr/source/lib/xmp_core/XMPIterator.hpp b/gpr/source/lib/xmp_core/XMPIterator.hpp
new file mode 100644
index 0000000..30146ae
--- /dev/null
+++ b/gpr/source/lib/xmp_core/XMPIterator.hpp
@@ -0,0 +1,144 @@
+#ifndef __XMPIterator_hpp__
+#define __XMPIterator_hpp__
+
+// =================================================================================================
+// Copyright 2003 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.
+// =================================================================================================
+
+#include "public/include/XMP_Environment.h"
+#include "public/include/XMP_Const.h"
+#include "XMPMeta.hpp"
+
+// =================================================================================================
+
+struct IterNode;
+typedef std::vector < IterNode > IterOffspring;
+typedef IterOffspring::iterator IterPos;
+
+typedef std::pair < IterPos, IterPos > IterPosPair;
+typedef std::vector < IterPosPair > IterPosStack;
+
+enum { // Values for the visitStage field, used to decide how to proceed past a node.
+ kIter_BeforeVisit = 0, // Have not visited this node at all.
+ kIter_VisitSelf = 1, // Have visited this node and returned its value/options portion.
+ kIter_VisitQualifiers = 2, // In the midst of visiting this node's qualifiers.
+ kIter_VisitChildren = 3 // In the midst of visiting this node's children.
+};
+
+struct IterNode {
+
+ XMP_OptionBits options;
+ XMP_VarString fullPath;
+ size_t leafOffset;
+ IterOffspring children, qualifiers;
+ XMP_Uns8 visitStage;
+ #if 0 // *** XMP_DebugBuild
+ XMP_StringPtr _pathPtr, _leafPtr; // *** Not working, need operator=?
+ #endif
+
+ IterNode() : options(0), leafOffset(0), visitStage(kIter_BeforeVisit)
+ {
+ #if 0 // *** XMP_DebugBuild
+ _pathPtr = _leafPtr = 0;
+ #endif
+ };
+
+ IterNode ( XMP_OptionBits _options, const XMP_VarString& _fullPath, size_t _leafOffset )
+ : options(_options), fullPath(_fullPath), leafOffset(_leafOffset), visitStage(kIter_BeforeVisit)
+ {
+ #if 0 // *** XMP_DebugBuild
+ _pathPtr = fullPath.c_str();
+ _leafPtr = _pathPtr + leafOffset;
+ #endif
+ };
+
+};
+
+struct IterInfo {
+
+ XMP_OptionBits options;
+ const XMPMeta * xmpObj;
+ XMP_VarString currSchema;
+ IterPos currPos, endPos;
+ IterPosStack ancestors;
+ IterNode tree;
+ #if 0 // *** XMP_DebugBuild
+ XMP_StringPtr _schemaPtr; // *** Not working, need operator=?
+ #endif
+
+ IterInfo() : options(0), xmpObj(0)
+ {
+ #if 0 // *** XMP_DebugBuild
+ _schemaPtr = 0;
+ #endif
+ };
+
+ IterInfo ( XMP_OptionBits _options, const XMPMeta * _xmpObj ) : options(_options), xmpObj(_xmpObj)
+ {
+ #if 0 // *** XMP_DebugBuild
+ _schemaPtr = 0;
+ #endif
+ };
+
+};
+
+// =================================================================================================
+
+class XMPIterator {
+public:
+
+ static bool
+ Initialize(); // ! For internal use only!
+
+ static void
+ Terminate() RELEASE_NO_THROW; // ! For internal use only!
+
+ XMPIterator ( const XMPMeta & xmpObj, // Construct a property iterator.
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_OptionBits options );
+
+ XMPIterator ( XMP_StringPtr schemaNS, // Construct a table iterator.
+ XMP_StringPtr propName,
+ XMP_OptionBits options );
+
+ virtual ~XMPIterator() RELEASE_NO_THROW;
+
+ bool
+ Next ( XMP_StringPtr * schemaNS,
+ XMP_StringLen * nsSize,
+ XMP_StringPtr * propPath,
+ XMP_StringLen * pathSize,
+ XMP_StringPtr * propValue,
+ XMP_StringLen * valueSize,
+ XMP_OptionBits * propOptions );
+
+ void
+ Skip ( XMP_OptionBits options );
+
+ // ! Expose so that wrappers and file static functions can see the data.
+
+ XMP_Int32 clientRefs; // ! Must be signed to allow decrement from 0.
+ XMP_ReadWriteLock lock;
+
+ IterInfo info;
+
+private:
+
+ // ! These are hidden on purpose:
+ XMPIterator() : clientRefs(0)
+ { XMP_Throw ( "Call to hidden constructor", kXMPErr_InternalFailure ); };
+ XMPIterator ( const XMPIterator & /* original */ ) : clientRefs(0)
+ { XMP_Throw ( "Call to hidden constructor", kXMPErr_InternalFailure ); };
+ void operator= ( const XMPIterator & /* rhs */ )
+ { XMP_Throw ( "Call to hidden operator=", kXMPErr_InternalFailure ); };
+
+};
+
+// =================================================================================================
+
+#endif // __XMPIterator_hpp__
diff --git a/gpr/source/lib/xmp_core/XMPMeta-GetSet.cpp b/gpr/source/lib/xmp_core/XMPMeta-GetSet.cpp
new file mode 100644
index 0000000..44f10a4
--- /dev/null
+++ b/gpr/source/lib/xmp_core/XMPMeta-GetSet.cpp
@@ -0,0 +1,1309 @@
+// =================================================================================================
+// Copyright 2003 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 "public/include/XMP_Environment.h" // ! This must be the first include!
+#include "XMPCore_Impl.hpp"
+
+#include "XMPMeta.hpp"
+#include "XMPIterator.hpp"
+#include "XMPUtils.hpp"
+
+#include "public/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
+ #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
+
+
+// *** 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
+
+ std::string newValue = value; // Need a local copy to tweak and not change node.value for errors.
+
+ XMP_Uns8* chPtr = (XMP_Uns8*) newValue.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 ) {
+ XMP_Uns32 cp = GetCodePoint ( (const XMP_Uns8 **) &chPtr ); // Throws for bad UTF-8.
+ if ( (cp == 0xFFFE) || (cp == 0xFFFF) ) {
+ XMP_Throw ( "U+FFFE and U+FFFF are not allowed in XML", kXMPErr_BadXML );
+ }
+ }
+
+ }
+
+ if ( XMP_PropIsQualifier(node->options) && (node->name == "xml:lang") ) NormalizeLangValue ( &newValue );
+
+ node->value.swap ( newValue );
+
+ #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, (kXMP_PropHasQualifiers | kXMP_PropHasLang) );
+ XMP_Node * langQual = new XMP_Node ( newItem, "xml:lang", kXMP_PropIsQualifier );
+
+ try { // ! Use SetNodeValue, not constructors above, to get the character checks.
+ SetNodeValue ( newItem, itemValue );
+ SetNodeValue ( langQual, itemLang );
+ } catch (...) {
+ delete newItem;
+ delete langQual;
+ throw;
+ }
+
+ 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) && (options != 0) ); // Enforced by wrapper.
+
+ // ! Special case check to make errors consistent if the array does not exist. The other array
+ // ! functions and existing array here (empty or not) already throw.
+ if ( (itemIndex <= 0) && (itemIndex != kXMP_ArrayLastItem) ) XMP_Throw ( "Array index must be larger than zero", kXMPErr_BadXPath );
+
+ XMP_VarString itemPath;
+ XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &itemPath );
+ return GetProperty ( schemaNS, itemPath.c_str(), 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) && (options != 0) ); // Enforced by wrapper.
+
+ XMP_VarString fieldPath;
+ XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath );
+ return GetProperty ( schemaNS, fieldPath.c_str(), 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) && (options != 0) ); // Enforced by wrapper.
+
+ XMP_VarString qualPath;
+ XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath );
+ return GetProperty ( schemaNS, qualPath.c_str(), 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_VarString fieldPath;
+ XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath );
+ SetProperty ( schemaNS, fieldPath.c_str(), 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_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 );
+
+ XMP_VarString qualPath;
+ XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath );
+ SetProperty ( schemaNS, qualPath.c_str(), 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_VarString itemPath;
+ XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &itemPath );
+ DeleteProperty ( schemaNS, itemPath.c_str() );
+
+} // 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_VarString fieldPath;
+ XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath );
+ DeleteProperty ( schemaNS, fieldPath.c_str() );
+
+} // 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_VarString qualPath;
+ XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath );
+ DeleteProperty ( schemaNS, qualPath.c_str() );
+
+} // 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_VarString itemPath;
+ XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &itemPath );
+ return DoesPropertyExist ( schemaNS, itemPath.c_str() );
+
+} // 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_VarString fieldPath;
+ XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath );
+ return DoesPropertyExist ( schemaNS, fieldPath.c_str() );
+
+} // 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_VarString qualPath;
+ XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath );
+ return DoesPropertyExist ( schemaNS, qualPath.c_str() );
+
+} // 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 )
+{
+ IgnoreParam(options);
+
+ 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 ( xdItem != NULL && 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 ( xdItem != NULL && 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 ( xdItem != NULL && 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
+
+// -------------------------------------------------------------------------------------------------
+// DeleteLocalizedText
+// -------------------
+
+void
+XMPMeta::DeleteLocalizedText ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr _genericLang,
+ XMP_StringPtr _specificLang )
+{
+ 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 LangAlt array and the selected array item.
+
+ XMP_Node * arrayNode = FindNode ( &tree, arrayPath, kXMP_ExistingOnly );
+ if ( arrayNode == 0 ) return;
+ size_t arraySize = arrayNode->children.size();
+
+ XMP_CLTMatch match;
+ XMP_Node * itemNode;
+
+ match = ChooseLocalizedText ( arrayNode, genericLang, specificLang, (const XMP_Node **) &itemNode );
+ if ( match != kXMP_CLT_SpecificMatch ) return;
+
+ size_t itemIndex = 0;
+ for ( ; itemIndex < arraySize; ++itemIndex ) {
+ if ( arrayNode->children[itemIndex] == itemNode ) break;
+ }
+ XMP_Enforce ( itemIndex < arraySize );
+
+ // Decide if the selected item is x-default or not, find relevant matching item.
+
+ bool itemIsXDefault = false;
+ if ( ! itemNode->qualifiers.empty() ) {
+ XMP_Node * qualNode = itemNode->qualifiers[0];
+ if ( (qualNode->name == "xml:lang") && (qualNode->value == "x-default") ) itemIsXDefault = true;
+ }
+
+ if ( itemIsXDefault && (itemIndex != 0) ) { // Enforce the x-default is first policy.
+ XMP_Node * temp = arrayNode->children[0];
+ arrayNode->children[0] = arrayNode->children[itemIndex];
+ arrayNode->children[itemIndex] = temp;
+ itemIndex = 0;
+ }
+
+ XMP_Node * assocNode = 0;
+ size_t assocIndex;
+ size_t assocIsXDefault = false;
+
+ if ( itemIsXDefault ) {
+
+ for ( assocIndex = 1; assocIndex < arraySize; ++assocIndex ) {
+ if ( arrayNode->children[assocIndex]->value == itemNode->value ) {
+ assocNode = arrayNode->children[assocIndex];
+ break;
+ }
+ }
+
+ } else if ( itemIndex > 0 ) {
+
+ XMP_Node * itemZero = arrayNode->children[0];
+ if ( itemZero->value == itemNode->value ) {
+ XMP_Node * qualNode = itemZero->qualifiers[0];
+ if ( (qualNode->name == "xml:lang") && (qualNode->value == "x-default") ) {
+ assocNode = arrayNode->children[0];
+ assocIndex = 0;
+ assocIsXDefault = true;
+ }
+ }
+
+ }
+
+ // Delete the appropriate nodes.
+
+ XMP_NodePtrPos arrayBegin = arrayNode->children.begin();
+
+ if ( assocNode == 0 ) {
+ arrayNode->children.erase ( arrayBegin + itemIndex );
+ } else if ( itemIndex < assocIndex ) {
+ arrayNode->children.erase ( arrayBegin + assocIndex );
+ arrayNode->children.erase ( arrayBegin + itemIndex );
+ } else {
+ arrayNode->children.erase ( arrayBegin + itemIndex );
+ arrayNode->children.erase ( arrayBegin + assocIndex );
+ }
+
+ delete itemNode;
+ if ( assocNode != 0 ) delete assocNode;
+
+} // DeleteLocalizedText
+
+// -------------------------------------------------------------------------------------------------
+// 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_Int64 tempValue64 = 0;
+ if ( GetProperty_Int64( schemaNS, propName, &tempValue64, options ) ) {
+ if ( tempValue64 < (XMP_Int64) Min_XMP_Int32 || tempValue64 > (XMP_Int64) Max_XMP_Int32 ) {
+ // overflow condition
+ XMP_Throw ( "Overflow condition", kXMPErr_BadValue );
+ } else {
+ *propValue = (XMP_Int32) tempValue64;
+ return true;
+ }
+ }
+ return false;
+
+} // 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 );
+ std::string propValueStr;
+ propValueStr.append( valueStr, valueLen );
+ XMPUtils::Trim( propValueStr );
+ *propValue = XMPUtils::ConvertToInt64 ( propValueStr.c_str() );
+ }
+ 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 );
+ std::string propValueStr;
+ propValueStr.append( valueStr, valueLen );
+ XMPUtils::Trim( propValueStr );
+ *propValue = XMPUtils::ConvertToFloat ( propValueStr.c_str() );
+ }
+ 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_VarString valueStr;
+ XMPUtils::ConvertFromBool ( propValue, &valueStr );
+ SetProperty ( schemaNS, propName, valueStr.c_str(), 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_VarString valueStr;
+ XMPUtils::ConvertFromInt ( propValue, "", &valueStr );
+ SetProperty ( schemaNS, propName, valueStr.c_str(), 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_VarString valueStr;
+ XMPUtils::ConvertFromInt64 ( propValue, "", &valueStr );
+ SetProperty ( schemaNS, propName, valueStr.c_str(), 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_VarString valueStr;
+ XMPUtils::ConvertFromFloat ( propValue, "", &valueStr );
+ SetProperty ( schemaNS, propName, valueStr.c_str(), 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_VarString valueStr;
+ XMPUtils::ConvertFromDate ( propValue, &valueStr );
+ SetProperty ( schemaNS, propName, valueStr.c_str(), options );
+
+} // SetProperty_Date
+
+// =================================================================================================
+
diff --git a/gpr/source/lib/xmp_core/XMPMeta-Parse.cpp b/gpr/source/lib/xmp_core/XMPMeta-Parse.cpp
new file mode 100644
index 0000000..28f628a
--- /dev/null
+++ b/gpr/source/lib/xmp_core/XMPMeta-Parse.cpp
@@ -0,0 +1,1277 @@
+// =================================================================================================
+// Copyright 2003 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 "public/include/XMP_Environment.h" // ! This must be the first include!
+#include "XMPCore_Impl.hpp"
+
+#include "XMPMeta.hpp"
+#include "XMPUtils.hpp"
+
+#include "UnicodeInlines.incl_cpp"
+#include "UnicodeConversions.hpp"
+#include "ExpatAdapter.hpp"
+
+#if XMP_DebugBuild
+ #include <iostream>
+#endif
+
+using namespace std;
+
+#if XMP_WinBuild
+ #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)
+ #pragma warning ( disable : 4996 ) // '...' was declared deprecated
+#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
+// =========================
+
+
+// =================================================================================================
+// Static Variables
+// ================
+
+#ifndef Trace_ParsingHackery
+ #define Trace_ParsingHackery 0
+#endif
+
+static const char * kReplaceLatin1[128] =
+ {
+
+ // The 0x80..0x9F range is undefined in Latin-1, but is defined in Windows code page 1252.
+ // The bytes 0x81, 0x8D, 0x8F, 0x90, and 0x9D are formally undefined by Windows 1252, but
+ // their conversion API maps them to U+0081, etc. These are in XML's RestrictedChar set, so
+ // we map them to a space.
+
+ "\xE2\x82\xAC", " ", "\xE2\x80\x9A", "\xC6\x92", // 0x80 .. 0x83
+ "\xE2\x80\x9E", "\xE2\x80\xA6", "\xE2\x80\xA0", "\xE2\x80\xA1", // 0x84 .. 0x87
+ "\xCB\x86", "\xE2\x80\xB0", "\xC5\xA0", "\xE2\x80\xB9", // 0x88 .. 0x8B
+ "\xC5\x92", " ", "\xC5\xBD", " ", // 0x8C .. 0x8F
+
+ " ", "\xE2\x80\x98", "\xE2\x80\x99", "\xE2\x80\x9C", // 0x90 .. 0x93
+ "\xE2\x80\x9D", "\xE2\x80\xA2", "\xE2\x80\x93", "\xE2\x80\x94", // 0x94 .. 0x97
+ "\xCB\x9C", "\xE2\x84\xA2", "\xC5\xA1", "\xE2\x80\xBA", // 0x98 .. 0x9B
+ "\xC5\x93", " ", "\xC5\xBE", "\xC5\xB8", // 0x9C .. 0x9F
+
+ // These are the UTF-8 forms of the official Latin-1 characters in the range 0xA0..0xFF. Not
+ // too surprisingly these map to U+00A0, etc. Which is the Unicode Latin Supplement range.
+
+ "\xC2\xA0", "\xC2\xA1", "\xC2\xA2", "\xC2\xA3", "\xC2\xA4", "\xC2\xA5", "\xC2\xA6", "\xC2\xA7", // 0xA0 .. 0xA7
+ "\xC2\xA8", "\xC2\xA9", "\xC2\xAA", "\xC2\xAB", "\xC2\xAC", "\xC2\xAD", "\xC2\xAE", "\xC2\xAF", // 0xA8 .. 0xAF
+
+ "\xC2\xB0", "\xC2\xB1", "\xC2\xB2", "\xC2\xB3", "\xC2\xB4", "\xC2\xB5", "\xC2\xB6", "\xC2\xB7", // 0xB0 .. 0xB7
+ "\xC2\xB8", "\xC2\xB9", "\xC2\xBA", "\xC2\xBB", "\xC2\xBC", "\xC2\xBD", "\xC2\xBE", "\xC2\xBF", // 0xB8 .. 0xBF
+
+ "\xC3\x80", "\xC3\x81", "\xC3\x82", "\xC3\x83", "\xC3\x84", "\xC3\x85", "\xC3\x86", "\xC3\x87", // 0xC0 .. 0xC7
+ "\xC3\x88", "\xC3\x89", "\xC3\x8A", "\xC3\x8B", "\xC3\x8C", "\xC3\x8D", "\xC3\x8E", "\xC3\x8F", // 0xC8 .. 0xCF
+
+ "\xC3\x90", "\xC3\x91", "\xC3\x92", "\xC3\x93", "\xC3\x94", "\xC3\x95", "\xC3\x96", "\xC3\x97", // 0xD0 .. 0xD7
+ "\xC3\x98", "\xC3\x99", "\xC3\x9A", "\xC3\x9B", "\xC3\x9C", "\xC3\x9D", "\xC3\x9E", "\xC3\x9F", // 0xD8 .. 0xDF
+
+ "\xC3\xA0", "\xC3\xA1", "\xC3\xA2", "\xC3\xA3", "\xC3\xA4", "\xC3\xA5", "\xC3\xA6", "\xC3\xA7", // 0xE0 .. 0xE7
+ "\xC3\xA8", "\xC3\xA9", "\xC3\xAA", "\xC3\xAB", "\xC3\xAC", "\xC3\xAD", "\xC3\xAE", "\xC3\xAF", // 0xE8 .. 0xEF
+
+ "\xC3\xB0", "\xC3\xB1", "\xC3\xB2", "\xC3\xB3", "\xC3\xB4", "\xC3\xB5", "\xC3\xB6", "\xC3\xB7", // 0xF0 .. 0xF7
+ "\xC3\xB8", "\xC3\xB9", "\xC3\xBA", "\xC3\xBB", "\xC3\xBC", "\xC3\xBD", "\xC3\xBE", "\xC3\xBF", // 0xF8 .. 0xFF
+
+ };
+
+
+// =================================================================================================
+// Local Utilities
+// ===============
+
+
+#define IsHexDigit(ch) ( (('0' <= (ch)) && ((ch) <= '9')) || (('A' <= (ch)) && ((ch) <= 'F')) )
+#define HexDigitValue(ch) ( (((ch) - '0') < 10) ? ((ch) - '0') : ((ch) - 'A' + 10) )
+
+
+// -------------------------------------------------------------------------------------------------
+// PickBestRoot
+// ------------
+//
+// Pick the first x:xmpmeta among multiple root candidates. If there aren't any, pick the first bare
+// rdf:RDF if that is allowed. The returned root is the rdf:RDF child if an x:xmpmeta element was
+// chosen. The search is breadth first, so a higher level candiate is chosen over a lower level one
+// that was textually earlier in the serialized XML.
+
+static const XML_Node * PickBestRoot ( const XML_Node & xmlParent, XMP_OptionBits options )
+{
+
+ // Look among this parent's content for x:xmpmeta. The recursion for x:xmpmeta is broader than
+ // the strictly defined choice, but gives us smaller code.
+ for ( size_t childNum = 0, childLim = xmlParent.content.size(); childNum < childLim; ++childNum ) {
+ const XML_Node * childNode = xmlParent.content[childNum];
+ if ( childNode->kind != kElemNode ) continue;
+ if ( (childNode->name == "x:xmpmeta") || (childNode->name == "x:xapmeta") ) return PickBestRoot ( *childNode, 0 );
+ }
+ // Look among this parent's content for a bare rdf:RDF if that is allowed.
+ if ( ! (options & kXMP_RequireXMPMeta) ) {
+ for ( size_t childNum = 0, childLim = xmlParent.content.size(); childNum < childLim; ++childNum ) {
+ const XML_Node * childNode = xmlParent.content[childNum];
+ if ( childNode->kind != kElemNode ) continue;
+ if ( childNode->name == "rdf:RDF" ) return childNode;
+ }
+ }
+
+ // Recurse into the content.
+ for ( size_t childNum = 0, childLim = xmlParent.content.size(); childNum < childLim; ++childNum ) {
+ const XML_Node * foundRoot = PickBestRoot ( *xmlParent.content[childNum], options );
+ if ( foundRoot != 0 ) return foundRoot;
+ }
+
+ return 0;
+
+} // PickBestRoot
+
+// -------------------------------------------------------------------------------------------------
+// FindRootNode
+// ------------
+//
+// Find the XML node that is the root of the XMP data tree. Generally this will be an outer node,
+// but it could be anywhere if a general XML document is parsed (e.g. SVG). The XML parser counted
+// all possible root nodes, and kept a pointer to the last one. If there is more than one possible
+// root use PickBestRoot to choose among them.
+//
+// If there is a root node, try to extract the version of the previous XMP toolkit.
+
+static const XML_Node * FindRootNode ( const XMLParserAdapter & xmlParser, XMP_OptionBits options )
+{
+ const XML_Node * rootNode = xmlParser.rootNode;
+
+ if ( xmlParser.rootCount > 1 ) rootNode = PickBestRoot ( xmlParser.tree, options );
+ if ( rootNode == 0 ) return 0;
+
+ XMP_Assert ( rootNode->name == "rdf:RDF" );
+
+ if ( (options & kXMP_RequireXMPMeta) &&
+ ((rootNode->parent == 0) ||
+ ((rootNode->parent->name != "x:xmpmeta") && (rootNode->parent->name != "x:xapmeta"))) ) return 0;
+
+ return rootNode;
+
+} // FindRootNode
+
+// -------------------------------------------------------------------------------------------------
+// NormalizeDCArrays
+// -----------------
+//
+// Undo the denormalization performed by the XMP used in Acrobat 5. If a Dublin Core array had only
+// one item, it was serialized as a simple property. The xml:lang attribute was dropped from an
+// alt-text item if the language was x-default.
+
+// *** This depends on the dc: namespace prefix.
+
+static void
+NormalizeDCArrays ( XMP_Node * xmpTree )
+{
+ XMP_Node * dcSchema = FindSchemaNode ( xmpTree, kXMP_NS_DC, kXMP_ExistingOnly );
+ if ( dcSchema == 0 ) return;
+
+ for ( size_t propNum = 0, propLimit = dcSchema->children.size(); propNum < propLimit; ++propNum ) {
+ XMP_Node * currProp = dcSchema->children[propNum];
+ XMP_OptionBits arrayForm = 0;
+
+ if ( ! XMP_PropIsSimple ( currProp->options ) ) continue; // Nothing to do if not simple.
+
+ if ( (currProp->name == "dc:creator" ) || // See if it is supposed to be an array.
+ (currProp->name == "dc:date" ) ) { // *** Think about an array of char* and a loop.
+ arrayForm = kXMP_PropArrayIsOrdered;
+ } else if (
+ (currProp->name == "dc:description" ) ||
+ (currProp->name == "dc:rights" ) ||
+ (currProp->name == "dc:title" ) ) {
+ arrayForm = kXMP_PropArrayIsAltText;
+ } else if (
+ (currProp->name == "dc:contributor" ) ||
+ (currProp->name == "dc:language" ) ||
+ (currProp->name == "dc:publisher" ) ||
+ (currProp->name == "dc:relation" ) ||
+ (currProp->name == "dc:subject" ) ||
+ (currProp->name == "dc:type" ) ) {
+ arrayForm = kXMP_PropValueIsArray;
+ }
+ if ( arrayForm == 0 ) continue; // Nothing to do if it isn't supposed to be an array.
+
+ arrayForm = VerifySetOptions ( arrayForm, 0 ); // Set the implicit array bits.
+ XMP_Node * newArray = new XMP_Node ( dcSchema, currProp->name.c_str(), arrayForm );
+ dcSchema->children[propNum] = newArray;
+
+ if ( currProp->value.empty() ) { // Don't add an empty item, leave the array empty.
+
+ delete ( currProp );
+
+ } else {
+
+ newArray->children.push_back ( currProp );
+ currProp->parent = newArray;
+ currProp->name = kXMP_ArrayItemName;
+
+ if ( XMP_ArrayIsAltText ( arrayForm ) && (! (currProp->options & kXMP_PropHasLang)) ) {
+ XMP_Node * newLang = new XMP_Node ( currProp, "xml:lang", "x-default", kXMP_PropIsQualifier );
+ currProp->options |= (kXMP_PropHasQualifiers | kXMP_PropHasLang);
+ if ( currProp->qualifiers.empty() ) { // *** Need a util?
+ currProp->qualifiers.push_back ( newLang );
+ } else {
+ currProp->qualifiers.insert ( currProp->qualifiers.begin(), newLang );
+ }
+ }
+
+ }
+
+ }
+
+} // NormalizeDCArrays
+
+
+// -------------------------------------------------------------------------------------------------
+// CompareAliasedSubtrees
+// ----------------------
+
+// *** Change to do some alias-specific setup, then use CompareSubtrees. One special case for
+// *** aliases is a simple to x-default alias, the options and qualifiers obviously differ.
+
+static void
+CompareAliasedSubtrees ( XMP_Node * aliasNode, XMP_Node * baseNode,
+ XMPMeta::ErrorCallbackInfo & errorCallback, bool outerCall = true )
+{
+ // ! The outermost call is special. The names almost certainly differ. The qualifiers (and
+ // ! hence options) will differ for an alias to the x-default item of a langAlt array.
+
+ if ( (aliasNode->value != baseNode->value) ||
+ (aliasNode->children.size() != baseNode->children.size()) ) {
+ // Keep things simple for now. Aliases are virtually unused, so this is very unlikely to
+ // happen. Recovery can be added later if it becomes important.
+ XMP_Error error(kXMPErr_BadXMP, "Mismatch between alias and base nodes");
+ errorCallback.NotifyClient ( kXMPErrSev_OperationFatal, error );
+ }
+
+ if ( ! outerCall ) {
+ if ( (aliasNode->name != baseNode->name) ||
+ (aliasNode->options != baseNode->options) ||
+ (aliasNode->qualifiers.size() != baseNode->qualifiers.size()) ) {
+ // Keep things simple for now. Aliases are virtually unused, so this is very unlikely to
+ // happen. Recovery can be added later if it becomes important.
+ XMP_Error error(kXMPErr_BadXMP, "Mismatch between alias and base nodes");
+ errorCallback.NotifyClient ( kXMPErrSev_OperationFatal, error );
+ }
+ }
+
+ for ( size_t childNum = 0, childLim = aliasNode->children.size(); childNum < childLim; ++childNum ) {
+ XMP_Node * aliasChild = aliasNode->children[childNum];
+ XMP_Node * baseChild = baseNode->children[childNum];
+ CompareAliasedSubtrees ( aliasChild, baseChild, errorCallback, false );
+ }
+
+ for ( size_t qualNum = 0, qualLim = aliasNode->qualifiers.size(); qualNum < qualLim; ++qualNum ) {
+ XMP_Node * aliasQual = aliasNode->qualifiers[qualNum];
+ XMP_Node * baseQual = baseNode->qualifiers[qualNum];
+ CompareAliasedSubtrees ( aliasQual, baseQual, errorCallback, false );
+ }
+
+} // CompareAliasedSubtrees
+
+
+// -------------------------------------------------------------------------------------------------
+// TransplantArrayItemAlias
+// ------------------------
+
+static void
+TransplantArrayItemAlias ( XMP_Node * oldParent, size_t oldNum, XMP_Node * newParent,
+ XMPMeta::ErrorCallbackInfo & errorCallback )
+{
+ XMP_Node * childNode = oldParent->children[oldNum];
+
+ if ( newParent->options & kXMP_PropArrayIsAltText ) {
+ if ( childNode->options & kXMP_PropHasLang ) {
+ // Keep things simple for now. Aliases are virtually unused, so this is very unlikely to
+ // happen. Recovery can be added later if it becomes important.
+ XMP_Error error(kXMPErr_BadXMP, "Alias to x-default already has a language qualifier");
+ errorCallback.NotifyClient ( kXMPErrSev_OperationFatal, error ); // *** Allow x-default.
+ }
+ childNode->options |= (kXMP_PropHasQualifiers | kXMP_PropHasLang);
+ XMP_Node * langQual = new XMP_Node ( childNode, "xml:lang", "x-default", kXMP_PropIsQualifier ); // *** AddLangQual util?
+ if ( childNode->qualifiers.empty() ) {
+ childNode->qualifiers.push_back ( langQual );
+ } else {
+ childNode->qualifiers.insert ( childNode->qualifiers.begin(), langQual );
+ }
+ }
+
+ oldParent->children.erase ( oldParent->children.begin() + oldNum );
+ childNode->name = kXMP_ArrayItemName;
+ childNode->parent = newParent;
+ if ( newParent->children.empty() ) {
+ newParent->children.push_back ( childNode );
+ } else {
+ newParent->children.insert ( newParent->children.begin(), childNode );
+ }
+
+} // TransplantArrayItemAlias
+
+
+// -------------------------------------------------------------------------------------------------
+// TransplantNamedAlias
+// --------------------
+
+static void
+TransplantNamedAlias ( XMP_Node * oldParent, size_t oldNum, XMP_Node * newParent, XMP_VarString & newName )
+{
+ XMP_Node * childNode = oldParent->children[oldNum];
+
+ oldParent->children.erase ( oldParent->children.begin() + oldNum );
+ childNode->name = newName;
+ childNode->parent = newParent;
+ newParent->children.push_back ( childNode );
+
+} // TransplantNamedAlias
+
+
+// -------------------------------------------------------------------------------------------------
+// MoveExplicitAliases
+// -------------------
+
+static void
+MoveExplicitAliases ( XMP_Node * tree, XMP_OptionBits parseOptions, XMPMeta::ErrorCallbackInfo & errorCallback )
+{
+ tree->options ^= kXMP_PropHasAliases;
+ const bool strictAliasing = ((parseOptions & kXMP_StrictAliasing) != 0);
+
+ // Visit all of the top level nodes looking for aliases. If there is no base, transplant the
+ // alias subtree. If there is a base and strict aliasing is on, make sure the alias and base
+ // subtrees match.
+
+ // ! Use "while" loops not "for" loops since both the schema and property loops can remove the
+ // ! current item from the vector being traversed. And don't increment the counter for a delete.
+
+ size_t schemaNum = 0;
+ while ( schemaNum < tree->children.size() ) {
+ XMP_Node * currSchema = tree->children[schemaNum];
+
+ size_t propNum = 0;
+ while ( propNum < currSchema->children.size() ) {
+ XMP_Node * currProp = currSchema->children[propNum];
+ if ( ! (currProp->options & kXMP_PropIsAlias) ) {
+ ++propNum;
+ continue;
+ }
+ currProp->options ^= kXMP_PropIsAlias;
+
+ // Find the base path, look for the base schema and root node.
+
+ XMP_AliasMapPos aliasPos = sRegisteredAliasMap->find ( currProp->name );
+ XMP_Assert ( aliasPos != sRegisteredAliasMap->end() );
+ XMP_ExpandedXPath & basePath = aliasPos->second;
+ XMP_OptionBits arrayOptions = (basePath[kRootPropStep].options & kXMP_PropArrayFormMask);
+
+ XMP_Node * baseSchema = FindSchemaNode ( tree, basePath[kSchemaStep].step.c_str(), kXMP_CreateNodes );
+ if ( baseSchema->options & kXMP_NewImplicitNode ) baseSchema->options ^= kXMP_NewImplicitNode;
+ XMP_Node * baseNode = FindChildNode ( baseSchema, basePath[kRootPropStep].step.c_str(), kXMP_ExistingOnly );
+
+ if ( baseNode == 0 ) {
+
+ if ( basePath.size() == 2 ) {
+ // A top-to-top alias, transplant the property.
+ TransplantNamedAlias ( currSchema, propNum, baseSchema, basePath[kRootPropStep].step );
+ } else {
+ // An alias to an array item, create the array and transplant the property.
+ baseNode = new XMP_Node ( baseSchema, basePath[kRootPropStep].step.c_str(), arrayOptions );
+ baseSchema->children.push_back ( baseNode );
+ TransplantArrayItemAlias ( currSchema, propNum, baseNode, errorCallback );
+ }
+
+ } else if ( basePath.size() == 2 ) {
+
+ // The base node does exist and this is a top-to-top alias. Check for conflicts if
+ // strict aliasing is on. Remove and delete the alias subtree.
+ if ( strictAliasing ) CompareAliasedSubtrees ( currProp, baseNode, errorCallback );
+ currSchema->children.erase ( currSchema->children.begin() + propNum );
+ delete currProp;
+
+ } else {
+
+ // This is an alias to an array item and the array exists. Look for the aliased item.
+ // Then transplant or check & delete as appropriate.
+
+ XMP_Node * itemNode = 0;
+ if ( arrayOptions & kXMP_PropArrayIsAltText ) {
+ XMP_Index xdIndex = LookupLangItem ( baseNode, *xdefaultName );
+ if ( xdIndex != -1 ) itemNode = baseNode->children[xdIndex];
+ } else if ( ! baseNode->children.empty() ) {
+ itemNode = baseNode->children[0];
+ }
+
+ if ( itemNode == 0 ) {
+ TransplantArrayItemAlias ( currSchema, propNum, baseNode, errorCallback );
+ } else {
+ if ( strictAliasing ) CompareAliasedSubtrees ( currProp, itemNode, errorCallback );
+ currSchema->children.erase ( currSchema->children.begin() + propNum );
+ delete currProp;
+ }
+
+ }
+
+ } // Property loop
+
+ // Increment the counter or remove an empty schema node.
+ if ( currSchema->children.size() > 0 ) {
+ ++schemaNum;
+ } else {
+ delete tree->children[schemaNum]; // ! Delete the schema node itself.
+ tree->children.erase ( tree->children.begin() + schemaNum );
+ }
+
+ } // Schema loop
+
+} // MoveExplicitAliases
+
+
+// -------------------------------------------------------------------------------------------------
+// FixGPSTimeStamp
+// ---------------
+
+static void
+FixGPSTimeStamp ( XMP_Node * exifSchema, XMP_Node * gpsDateTime )
+{
+ XMP_DateTime binGPSStamp;
+ try {
+ XMPUtils::ConvertToDate ( gpsDateTime->value.c_str(), &binGPSStamp );
+ } catch ( ... ) {
+ return; // Don't let a bad date stop other things.
+ }
+ if ( (binGPSStamp.year != 0) || (binGPSStamp.month != 0) || (binGPSStamp.day != 0) ) return;
+
+ XMP_Node * otherDate = FindChildNode ( exifSchema, "exif:DateTimeOriginal", kXMP_ExistingOnly );
+ if ( otherDate == 0 ) otherDate = FindChildNode ( exifSchema, "exif:DateTimeDigitized", kXMP_ExistingOnly );
+ if ( otherDate == 0 ) return;
+
+ XMP_DateTime binOtherDate;
+ try {
+ XMPUtils::ConvertToDate ( otherDate->value.c_str(), &binOtherDate );
+ } catch ( ... ) {
+ return; // Don't let a bad date stop other things.
+ }
+
+ binGPSStamp.year = binOtherDate.year;
+ binGPSStamp.month = binOtherDate.month;
+ binGPSStamp.day = binOtherDate.day;
+
+ XMPUtils::ConvertFromDate ( binGPSStamp, &gpsDateTime->value );
+
+} // FixGPSTimeStamp
+
+
+// -------------------------------------------------------------------------------------------------
+// MigrateAudioCopyright
+// ---------------------
+//
+// The initial support for WAV files mapped a legacy ID3 audio copyright into a new xmpDM:copyright
+// property. This is special case code to migrate that into dc:rights['x-default']. The rules:
+//
+// 1. If there is no dc:rights array, or an empty array -
+// Create one with dc:rights['x-default'] set from double linefeed and xmpDM:copyright.
+//
+// 2. If there is a dc:rights array but it has no x-default item -
+// Create an x-default item as a copy of the first item then apply rule #3.
+//
+// 3. If there is a dc:rights array with an x-default item, look for a double linefeed in the value.
+// A. If no double linefeed, compare the x-default value to the xmpDM:copyright value.
+// A1. If they match then leave the x-default value alone.
+// A2. Otherwise, append a double linefeed and the xmpDM:copyright value to the x-default value.
+// B. If there is a double linefeed, compare the trailing text to the xmpDM:copyright value.
+// B1. If they match then leave the x-default value alone.
+// B2. Otherwise, replace the trailing x-default text with the xmpDM:copyright value.
+//
+// 4. In all cases, delete the xmpDM:copyright property.
+
+static void
+MigrateAudioCopyright ( XMPMeta * xmp, XMP_Node * dmCopyright )
+{
+
+ try {
+
+ std::string & dmValue = dmCopyright->value;
+ static const char * kDoubleLF = "\xA\xA";
+
+ XMP_Node * dcSchema = FindSchemaNode ( &xmp->tree, kXMP_NS_DC, kXMP_CreateNodes );
+ XMP_Node * dcRightsArray = FindChildNode ( dcSchema, "dc:rights", kXMP_ExistingOnly );
+
+ if ( (dcRightsArray == 0) || dcRightsArray->children.empty() ) {
+
+ // 1. No dc:rights array, create from double linefeed and xmpDM:copyright.
+ dmValue.insert ( 0, kDoubleLF );
+ xmp->SetLocalizedText ( kXMP_NS_DC, "rights", "", "x-default", dmValue.c_str(), 0 );
+
+ } else {
+
+ std::string xdefaultStr ( "x-default" );
+
+ XMP_Index xdIndex = LookupLangItem ( dcRightsArray, xdefaultStr );
+
+ if ( xdIndex < 0 ) {
+ // 2. No x-default item, create from the first item.
+ XMP_StringPtr firstValue = dcRightsArray->children[0]->value.c_str();
+ xmp->SetLocalizedText ( kXMP_NS_DC, "rights", "", "x-default", firstValue, 0 );
+ xdIndex = LookupLangItem ( dcRightsArray, xdefaultStr );
+ }
+
+ // 3. Look for a double linefeed in the x-default value.
+ XMP_Assert ( xdIndex == 0 );
+ std::string & defaultValue = dcRightsArray->children[xdIndex]->value;
+ XMP_Index lfPos = defaultValue.find ( kDoubleLF );
+
+ if ( lfPos < 0 ) {
+
+ // 3A. No double LF, compare whole values.
+ if ( dmValue != defaultValue ) {
+ // 3A2. Append the xmpDM:copyright to the x-default item.
+ defaultValue += kDoubleLF;
+ defaultValue += dmValue;
+ }
+
+ } else {
+
+ // 3B. Has double LF, compare the tail.
+ if ( defaultValue.compare ( lfPos+2, std::string::npos, dmValue ) != 0 ) {
+ // 3B2. Replace the x-default tail.
+ defaultValue.replace ( lfPos+2, std::string::npos, dmValue );
+ }
+
+ }
+
+ }
+
+ // 4. Get rid of the xmpDM:copyright.
+ xmp->DeleteProperty ( kXMP_NS_DM, "copyright" );
+
+ } catch ( ... ) {
+ // Don't let failures (like a bad dc:rights form) stop other cleanup.
+ }
+
+} // MigrateAudioCopyright
+
+
+// -------------------------------------------------------------------------------------------------
+// RepairAltText
+// -------------
+//
+// Make sure that the array is well-formed AltText. Each item must be simple and have an xml:lang
+// qualifier. If repairs are needed, keep simple non-empty items by adding the xml:lang.
+
+static void
+RepairAltText ( XMP_Node & tree, XMP_StringPtr schemaNS, XMP_StringPtr arrayName )
+{
+ XMP_Node * schemaNode = FindSchemaNode ( &tree, schemaNS, kXMP_ExistingOnly );
+ if ( schemaNode == 0 ) return;
+
+ XMP_Node * arrayNode = FindChildNode ( schemaNode, arrayName, kXMP_ExistingOnly );
+ if ( (arrayNode == 0) || XMP_ArrayIsAltText ( arrayNode->options ) ) return; // Already OK.
+
+ if ( ! XMP_PropIsArray ( arrayNode->options ) ) return; // ! Not even an array, leave it alone.
+ // *** Should probably change simple values to LangAlt with 'x-default' item.
+
+ arrayNode->options |= (kXMP_PropArrayIsOrdered | kXMP_PropArrayIsAlternate | kXMP_PropArrayIsAltText);
+
+ for ( int i = arrayNode->children.size()-1; i >= 0; --i ) { // ! Need a signed index type.
+
+ XMP_Node * currChild = arrayNode->children[i];
+
+ if ( ! XMP_PropIsSimple ( currChild->options ) ) {
+
+ // Delete non-simple children.
+ delete ( currChild );
+ arrayNode->children.erase ( arrayNode->children.begin() + i );
+
+ } else if ( ! XMP_PropHasLang ( currChild->options ) ) {
+
+ if ( currChild->value.empty() ) {
+
+ // Delete empty valued children that have no xml:lang.
+ delete ( currChild );
+ arrayNode->children.erase ( arrayNode->children.begin() + i );
+
+ } else {
+
+ // Add an xml:lang qualifier with the value "x-repair".
+ XMP_Node * repairLang = new XMP_Node ( currChild, "xml:lang", "x-repair", kXMP_PropIsQualifier );
+ if ( currChild->qualifiers.empty() ) {
+ currChild->qualifiers.push_back ( repairLang );
+ } else {
+ currChild->qualifiers.insert ( currChild->qualifiers.begin(), repairLang );
+ }
+ currChild->options |= (kXMP_PropHasQualifiers | kXMP_PropHasLang);
+
+ }
+
+ }
+
+ }
+
+} // RepairAltText
+
+
+// -------------------------------------------------------------------------------------------------
+// TouchUpDataModel
+// ----------------
+
+static void
+TouchUpDataModel ( XMPMeta * xmp, XMPMeta::ErrorCallbackInfo & errorCallback )
+{
+ XMP_Node & tree = xmp->tree;
+
+ // Do special case touch ups for certain schema.
+
+ XMP_Node * currSchema = 0;
+
+ currSchema = FindSchemaNode ( &tree, kXMP_NS_EXIF, kXMP_ExistingOnly );
+ if ( currSchema != 0 ) {
+
+ // Do a special case fix for exif:GPSTimeStamp.
+ XMP_Node * gpsDateTime = FindChildNode ( currSchema, "exif:GPSTimeStamp", kXMP_ExistingOnly );
+ if ( gpsDateTime != 0 ) FixGPSTimeStamp ( currSchema, gpsDateTime );
+
+ // *** Should probably have RepairAltText change simple values to LangAlt with 'x-default' item.
+ // *** For now just do this for exif:UserComment, the one case we know about, late in cycle fix.
+ XMP_Node * userComment = FindChildNode ( currSchema, "exif:UserComment", kXMP_ExistingOnly );
+ if ( (userComment != 0) && XMP_PropIsSimple ( userComment->options ) ) {
+ XMP_Node * newChild = new XMP_Node ( userComment, kXMP_ArrayItemName,
+ userComment->value.c_str(), userComment->options );
+ newChild->qualifiers.swap ( userComment->qualifiers );
+ if ( ! XMP_PropHasLang ( newChild->options ) ) {
+ XMP_Node * langQual = new XMP_Node ( newChild, "xml:lang", "x-default", kXMP_PropIsQualifier );
+ newChild->qualifiers.insert ( newChild->qualifiers.begin(), langQual );
+ newChild->options |= (kXMP_PropHasQualifiers | kXMP_PropHasLang);
+ }
+ userComment->value.erase();
+ userComment->options = kXMP_PropArrayFormMask; // ! Happens to have all the right bits.
+ userComment->children.push_back ( newChild );
+ }
+
+ }
+
+ currSchema = FindSchemaNode ( &tree, kXMP_NS_DM, kXMP_ExistingOnly );
+ if ( currSchema != 0 ) {
+ // Do a special case migration of xmpDM:copyright to dc:rights['x-default']. Do this before
+ // the dc: touch up since it can affect the dc: schema.
+ XMP_Node * dmCopyright = FindChildNode ( currSchema, "xmpDM:copyright", kXMP_ExistingOnly );
+ if ( dmCopyright != 0 ) MigrateAudioCopyright ( xmp, dmCopyright );
+ }
+
+ currSchema = FindSchemaNode ( &tree, kXMP_NS_DC, kXMP_ExistingOnly );
+ if ( currSchema != 0 ) {
+ // Do a special case fix for dc:subject, make sure it is an unordered array.
+ XMP_Node * dcSubject = FindChildNode ( currSchema, "dc:subject", kXMP_ExistingOnly );
+ if ( dcSubject != 0 ) {
+ XMP_OptionBits keepMask = ~(kXMP_PropArrayIsOrdered | kXMP_PropArrayIsAlternate | kXMP_PropArrayIsAltText);
+ dcSubject->options &= keepMask; // Make sure any ordered array bits are clear.
+ }
+ }
+
+ // Fix any broken AltText arrays that we know about.
+
+ RepairAltText ( tree, kXMP_NS_DC, "dc:description" ); // ! Note inclusion of prefixes for direct node lookup!
+ RepairAltText ( tree, kXMP_NS_DC, "dc:rights" );
+ RepairAltText ( tree, kXMP_NS_DC, "dc:title" );
+ RepairAltText ( tree, kXMP_NS_XMP_Rights, "xmpRights:UsageTerms" );
+ RepairAltText ( tree, kXMP_NS_EXIF, "exif:UserComment" );
+
+ // Tweak old XMP: Move an instance ID from rdf:about to the xmpMM:InstanceID property. An old
+ // instance ID usually looks like "uuid:bac965c4-9d87-11d9-9a30-000d936b79c4", plus InDesign
+ // 3.0 wrote them like "bac965c4-9d87-11d9-9a30-000d936b79c4". If the name looks like a UUID
+ // simply move it to xmpMM:InstanceID, don't worry about any existing xmpMM:InstanceID. Both
+ // will only be present when a newer file with the xmpMM:InstanceID property is updated by an
+ // old app that uses rdf:about.
+
+ if ( ! tree.name.empty() ) {
+
+ bool nameIsUUID = false;
+ XMP_StringPtr nameStr = tree.name.c_str();
+
+ if ( XMP_LitNMatch ( nameStr, "uuid:", 5 ) ) {
+
+ nameIsUUID = true;
+
+ } else if ( tree.name.size() == 36 ) {
+
+ nameIsUUID = true; // ! Assume true, we'll set it to false below if not.
+ for ( int i = 0; i < 36; ++i ) {
+ char ch = nameStr[i];
+ if ( ch == '-' ) {
+ if ( (i == 8) || (i == 13) || (i == 18) || (i == 23) ) continue;
+ nameIsUUID = false;
+ break;
+ } else {
+ if ( (('0' <= ch) && (ch <= '9')) || (('a' <= ch) && (ch <= 'z')) ) continue;
+ nameIsUUID = false;
+ break;
+ }
+ }
+
+ }
+
+ if ( nameIsUUID ) {
+
+ XMP_ExpandedXPath expPath;
+ ExpandXPath ( kXMP_NS_XMP_MM, "InstanceID", &expPath );
+ XMP_Node * idNode = FindNode ( &tree, expPath, kXMP_CreateNodes, 0 );
+ if ( idNode == 0 ) XMP_Throw ( "Failure creating xmpMM:InstanceID", kXMPErr_InternalFailure );
+
+ idNode->options = 0; // Clobber any existing xmpMM:InstanceID.
+ idNode->value = tree.name;
+ idNode->RemoveChildren();
+ idNode->RemoveQualifiers();
+
+ tree.name.erase();
+
+ }
+
+ }
+
+} // TouchUpDataModel
+
+
+// -------------------------------------------------------------------------------------------------
+// DetermineInputEncoding
+// ----------------------
+//
+// Try to determine the character encoding, making a guess if the input is too short. We make some
+// simplifying assumtions: the first character must be U+FEFF or ASCII, U+0000 is not allowed. The
+// XML 1.1 spec is even more strict, UTF-16 XML documents must begin with U+FEFF, and the first
+// "real" character must be '<'. Ignoring the XML declaration, the first XML character could be '<',
+// space, tab, CR, or LF.
+//
+// The possible input sequences are:
+//
+// Cases with U+FEFF
+// EF BB BF -- - UTF-8
+// FE FF -- -- - Big endian UTF-16
+// 00 00 FE FF - Big endian UTF 32
+// FF FE 00 00 - Little endian UTF-32
+// FF FE -- -- - Little endian UTF-16
+//
+// Cases with ASCII
+// nn mm -- -- - UTF-8 -
+// 00 00 00 nn - Big endian UTF-32
+// 00 nn -- -- - Big endian UTF-16
+// nn 00 00 00 - Little endian UTF-32
+// nn 00 -- -- - Little endian UTF-16
+//
+// ! We don't check for full patterns, or for errors. We just check enough to determine what the
+// ! only possible (or reasonable) case would be.
+
+static XMP_OptionBits
+DetermineInputEncoding ( const XMP_Uns8 * buffer, size_t length )
+{
+ if ( length < 2 ) return kXMP_EncodeUTF8;
+
+ XMP_Uns8 * uniChar = (XMP_Uns8*)buffer; // ! Make sure comparisons are unsigned.
+
+ if ( uniChar[0] == 0 ) {
+
+ // These cases are:
+ // 00 nn -- -- - Big endian UTF-16
+ // 00 00 00 nn - Big endian UTF-32
+ // 00 00 FE FF - Big endian UTF 32
+
+ if ( (length < 4) || (uniChar[1] != 0) ) return kXMP_EncodeUTF16Big;
+ return kXMP_EncodeUTF32Big;
+
+ } else if ( uniChar[0] < 0x80 ) {
+
+ // These cases are:
+ // nn mm -- -- - UTF-8, includes EF BB BF case
+ // nn 00 00 00 - Little endian UTF-32
+ // nn 00 -- -- - Little endian UTF-16
+
+ if ( uniChar[1] != 0 ) return kXMP_EncodeUTF8;
+ if ( (length < 4) || (uniChar[2] != 0) ) return kXMP_EncodeUTF16Little;
+ return kXMP_EncodeUTF32Little;
+
+ } else {
+
+ // These cases are:
+ // EF BB BF -- - UTF-8
+ // FE FF -- -- - Big endian UTF-16
+ // FF FE 00 00 - Little endian UTF-32
+ // FF FE -- -- - Little endian UTF-16
+
+ if ( uniChar[0] == 0xEF ) return kXMP_EncodeUTF8;
+ if ( uniChar[0] == 0xFE ) return kXMP_EncodeUTF16Big;
+ if ( (length < 4) || (uniChar[2] != 0) ) return kXMP_EncodeUTF16Little;
+ return kXMP_EncodeUTF32Little;
+
+ }
+
+} // DetermineInputEncoding
+
+
+// -------------------------------------------------------------------------------------------------
+// CountUTF8
+// ---------
+//
+// Look for a valid multi-byte UTF-8 sequence and return its length. Returns 0 for an invalid UTF-8
+// sequence. Returns a negative value for a partial valid sequence at the end of the buffer.
+//
+// The checking is not strict. We simply count the number of high order 1 bits in the first byte,
+// then look for n-1 following bytes whose high order 2 bits are 1 and 0. We do not check for a
+// minimal length representation of the codepoint, or that the codepoint is defined by Unicode.
+
+static int
+CountUTF8 ( const XMP_Uns8 * charStart, const XMP_Uns8 * bufEnd )
+{
+ XMP_Assert ( charStart < bufEnd ); // Catch this in debug builds.
+ if ( charStart >= bufEnd ) return 0; // Don't run-on in release builds.
+ if ( (*charStart & 0xC0) != 0xC0 ) return 0; // Must have at least 2 high bits set.
+
+ int byteCount = 2;
+ XMP_Uns8 firstByte = *charStart;
+ for ( firstByte = firstByte << 2; (firstByte & 0x80) != 0; firstByte = firstByte << 1 ) ++byteCount;
+
+ if ( (charStart + byteCount) > bufEnd ) return -byteCount;
+
+ for ( int i = 1; i < byteCount; ++i ) {
+ if ( (charStart[i] & 0xC0) != 0x80 ) return 0;
+ }
+
+ return byteCount;
+
+} // CountUTF8
+
+
+// -------------------------------------------------------------------------------------------------
+// CountControlEscape
+// ------------------
+//
+// Look for a numeric escape sequence for a "prohibited" ASCII control character. These are 0x7F,
+// and the range 0x00..0x1F except for tab/LF/CR. Return 0 if this is definitely not a numeric
+// escape, the length of the escape if found, or a negative value for a partial escape.
+
+static int
+CountControlEscape ( const XMP_Uns8 * escStart, const XMP_Uns8 * bufEnd )
+{
+ XMP_Assert ( escStart < bufEnd ); // Catch this in debug builds.
+ if ( escStart >= bufEnd ) return 0; // Don't run-on in release builds.
+ XMP_Assert ( *escStart == '&' );
+
+ size_t tailLen = bufEnd - escStart;
+ if ( tailLen < 5 ) return -1; // Don't need a more thorough check, we'll catch it on the next pass.
+
+ if ( strncmp ( (char*)escStart, "&#x", 3 ) != 0 ) return 0;
+
+ XMP_Uns8 escValue = 0;
+ const XMP_Uns8 * escPos = escStart + 3;
+
+ if ( ('0' <= *escPos) && (*escPos <= '9') ) {
+ escValue = *escPos - '0';
+ ++escPos;
+ } else if ( ('A' <= *escPos) && (*escPos <= 'F') ) {
+ escValue = *escPos - 'A' + 10;
+ ++escPos;
+ } else if ( ('a' <= *escPos) && (*escPos <= 'f') ) {
+ escValue = *escPos - 'a' + 10;
+ ++escPos;
+ }
+
+ if ( ('0' <= *escPos) && (*escPos <= '9') ) {
+ escValue = (escValue << 4) + (*escPos - '0');
+ ++escPos;
+ } else if ( ('A' <= *escPos) && (*escPos <= 'F') ) {
+ escValue = (escValue << 4) + (*escPos - 'A' + 10);
+ ++escPos;
+ } else if ( ('a' <= *escPos) && (*escPos <= 'f') ) {
+ escValue = (escValue << 4) + (*escPos - 'a' + 10);
+ ++escPos;
+ }
+
+ if ( escPos == bufEnd ) return -1; // Partial escape.
+ if ( *escPos != ';' ) return 0;
+
+ size_t escLen = escPos - escStart + 1;
+ if ( escLen < 5 ) return 0; // ! Catch "&#x;".
+
+ if ( (escValue == kTab) || (escValue == kLF) || (escValue == kCR) ) return 0; // An allowed escape.
+
+ return escLen; // Found a full "prohibited" numeric escape.
+
+} // CountControlEscape
+
+
+// -------------------------------------------------------------------------------------------------
+// ProcessUTF8Portion
+// ------------------
+//
+// Early versions of the XMP spec mentioned allowing ISO Latin-1 input. There are also problems with
+// some clients placing ASCII control characters within XMP values. This is an XML problem, the XML
+// spec only allows tab (0x09), LF (0x0A), and CR (0x0D) from the 0x00..0x1F range. As a concession
+// to this we scan 8-bit input for byte sequences that are not valid UTF-8 or in the 0x00..0x1F
+// range and replace each byte as follows:
+// 0x00..0x1F - Replace with a space, except for tab, CR, and LF.
+// 0x7F - Replace with a space. This is ASCII Delete, not allowed by ISO Latin-1.
+// 0x80..0x9F - Replace with the UTF-8 for a corresponding Unicode character.
+// 0xA0..0XFF - Replace with the UTF-8 for a corresponding Unicode character.
+//
+// The 0x80..0x9F range is not defined by Latin-1. But the Windows 1252 code page defines these and
+// is otherwise the same as Latin-1.
+//
+// For at least historical compatibility reasons we also find and replace singly escaped ASCII
+// control characters. The Expat parser we're using does not allow numeric escapes like "&#x10;".
+// The XML spec is clear that raw controls are not allowed (in the RestrictedChar set), but it isn't
+// as clear about numeric escapes for them. At any rate, Expat complains, so we treat the numeric
+// escapes like raw characters and replace them with a space.
+//
+// We check for 1 or 2 hex digits ("&#x9;" or "&#x09;") and upper or lower case ("&#xA;" or "&#xa;").
+// The full escape sequence is 5 or 6 bytes.
+
+static size_t
+ProcessUTF8Portion ( XMLParserAdapter * xmlParser,
+ const XMP_Uns8 * buffer,
+ size_t length,
+ bool last )
+{
+ const XMP_Uns8 * bufEnd = buffer + length;
+
+ const XMP_Uns8 * spanStart = buffer;
+ const XMP_Uns8 * spanEnd;
+
+ for ( spanEnd = spanStart; spanEnd < bufEnd; ++spanEnd ) {
+
+ if ( (0x20 <= *spanEnd) && (*spanEnd <= 0x7E) && (*spanEnd != '&') ) continue; // A regular ASCII character.
+
+ if ( *spanEnd >= 0x80 ) {
+
+ // See if this is a multi-byte UTF-8 sequence, or a Latin-1 character to replace.
+
+ int uniLen = CountUTF8 ( spanEnd, bufEnd );
+
+ if ( uniLen > 0 ) {
+
+ // A valid UTF-8 character, keep it as-is.
+ spanEnd += uniLen - 1; // ! The loop increment will put back the +1.
+
+ } else if ( (uniLen < 0) && (! last) ) {
+
+ // Have a partial UTF-8 character at the end of the buffer and more input coming.
+ xmlParser->ParseBuffer ( spanStart, (spanEnd - spanStart), false );
+ return (spanEnd - buffer);
+
+ } else {
+
+ // Not a valid UTF-8 sequence. Replace the first byte with the Latin-1 equivalent.
+ xmlParser->ParseBuffer ( spanStart, (spanEnd - spanStart), false );
+ const char * replacement = kReplaceLatin1 [ *spanEnd - 0x80 ];
+ xmlParser->ParseBuffer ( replacement, strlen ( replacement ), false );
+ spanStart = spanEnd + 1; // ! The loop increment will do "spanEnd = spanStart".
+
+ }
+
+ } else if ( (*spanEnd < 0x20) || (*spanEnd == 0x7F) ) {
+
+ // Replace ASCII controls other than tab, LF, and CR with a space.
+
+ if ( (*spanEnd == kTab) || (*spanEnd == kLF) || (*spanEnd == kCR) ) continue;
+
+ xmlParser->ParseBuffer ( spanStart, (spanEnd - spanStart), false );
+ xmlParser->ParseBuffer ( " ", 1, false );
+ spanStart = spanEnd + 1; // ! The loop increment will do "spanEnd = spanStart".
+
+ } else {
+
+ // See if this is a numeric escape sequence for a prohibited ASCII control.
+
+ XMP_Assert ( *spanEnd == '&' );
+ int escLen = CountControlEscape ( spanEnd, bufEnd );
+
+ if ( escLen < 0 ) {
+
+ // Have a partial numeric escape in this buffer, wait for more input.
+ if ( last ) continue; // No more buffers, not an escape, absorb as normal input.
+ xmlParser->ParseBuffer ( spanStart, (spanEnd - spanStart), false );
+ return (spanEnd - buffer);
+
+ } else if ( escLen > 0 ) {
+
+ // Have a complete numeric escape to replace.
+ xmlParser->ParseBuffer ( spanStart, (spanEnd - spanStart), false );
+ xmlParser->ParseBuffer ( " ", 1, false );
+ spanStart = spanEnd + escLen;
+ spanEnd = spanStart - 1; // ! The loop continuation will increment spanEnd!
+
+ }
+
+ }
+
+ }
+
+ XMP_Assert ( spanEnd == bufEnd );
+
+ if ( spanStart < bufEnd ) xmlParser->ParseBuffer ( spanStart, (spanEnd - spanStart), false );
+ if ( last ) xmlParser->ParseBuffer ( " ", 1, true );
+
+ return length;
+
+} // ProcessUTF8Portion
+
+
+// -------------------------------------------------------------------------------------------------
+// ProcessXMLBuffer
+// ----------------
+//
+// Process one buffer of XML. Returns false if this input is put into the pending input buffer.
+
+bool XMPMeta::ProcessXMLBuffer ( XMP_StringPtr buffer, XMP_StringLen xmpSize, bool lastClientCall )
+{
+
+ // Determine the character encoding before doing any real parsing. This is needed to do the
+ // 8-bit special processing. This has to be checked on every call, not just the first, in
+ // order to handle the edge case of single byte buffers.
+
+ XMLParserAdapter & parser = *this->xmlParser;
+
+ if ( parser.charEncoding == XMP_OptionBits(-1) ) {
+
+ if ( (parser.pendingCount == 0) && (xmpSize >= kXMLPendingInputMax) ) {
+
+ // This ought to be the common case, the first buffer is big enough.
+ parser.charEncoding = DetermineInputEncoding ( (XMP_Uns8*)buffer, xmpSize );
+
+ } else {
+
+ // Try to fill the pendingInput buffer before calling DetermineInputEncoding.
+
+ size_t pendingOverlap = kXMLPendingInputMax - parser.pendingCount;
+ if ( pendingOverlap > xmpSize ) pendingOverlap = xmpSize;
+
+ memcpy ( &parser.pendingInput[parser.pendingCount], buffer, pendingOverlap ); // AUDIT: Count is safe.
+ buffer += pendingOverlap;
+ xmpSize -= pendingOverlap;
+ parser.pendingCount += pendingOverlap;
+
+ if ( (! lastClientCall) && (parser.pendingCount < kXMLPendingInputMax) ) return false; // Wait for the next buffer.
+ parser.charEncoding = DetermineInputEncoding ( parser.pendingInput, parser.pendingCount );
+
+ #if Trace_ParsingHackery
+ fprintf ( stderr, "XMP Character encoding is %d\n", parser.charEncoding );
+ #endif
+
+ }
+
+ }
+
+ // We have the character encoding. Process UTF-16 and UTF-32 as is. UTF-8 needs special
+ // handling to take care of things like ISO Latin-1 or unescaped ASCII controls.
+
+ XMP_Assert ( parser.charEncoding != XMP_OptionBits(-1) );
+
+ if ( parser.charEncoding != kXMP_EncodeUTF8 ) {
+
+ if ( parser.pendingCount > 0 ) {
+ // Might have pendingInput from the above portion to determine the character encoding.
+ parser.ParseBuffer ( parser.pendingInput, parser.pendingCount, false );
+ }
+ parser.ParseBuffer ( buffer, xmpSize, lastClientCall );
+
+ } else {
+
+ #if Trace_ParsingHackery
+ fprintf ( stderr, "Parsing %d bytes @ %.8X, %s, %d pending, context: %.8s\n",
+ xmpSize, buffer, (lastClientCall ? "last" : "not last"), parser.pendingCount, buffer );
+ #endif
+
+ // The UTF-8 processing is a bit complex due to the need to tolerate ISO Latin-1 input.
+ // This is done by scanning the input for byte sequences that are not valid UTF-8,
+ // assuming they are Latin-1 characters in the range 0x80..0xFF. This requires saving a
+ // pending input buffer to handle partial UTF-8 sequences at the end of a buffer.
+
+ while ( parser.pendingCount > 0 ) {
+
+ // We've got some leftover input, process it first then continue with the current
+ // buffer. Try to fill the pendingInput buffer before parsing further. We use a loop
+ // for wierd edge cases like a 2 byte input buffer, using 1 byte for pendingInput,
+ // then having a partial UTF-8 end and need to absorb more.
+
+ size_t pendingOverlap = kXMLPendingInputMax - parser.pendingCount;
+ if ( pendingOverlap > xmpSize ) pendingOverlap = xmpSize;
+
+ memcpy ( &parser.pendingInput[parser.pendingCount], buffer, pendingOverlap ); // AUDIT: Count is safe.
+ parser.pendingCount += pendingOverlap;
+ buffer += pendingOverlap;
+ xmpSize -= pendingOverlap;
+
+ if ( (! lastClientCall) && (parser.pendingCount < kXMLPendingInputMax) ) return false; // Wait for the next buffer.
+ size_t bytesDone = ProcessUTF8Portion ( xmlParser, parser.pendingInput, parser.pendingCount, lastClientCall );
+ size_t bytesLeft = parser.pendingCount - bytesDone;
+
+ #if Trace_ParsingHackery
+ fprintf ( stderr, " ProcessUTF8Portion handled %d pending bytes\n", bytesDone );
+ #endif
+
+ if ( bytesDone == parser.pendingCount ) {
+
+ // Done with all of the pending input, move on to the current buffer.
+ parser.pendingCount = 0;
+
+ } else if ( bytesLeft <= pendingOverlap ) {
+
+ // The leftover pending input all came from the current buffer. Exit this loop.
+ buffer -= bytesLeft;
+ xmpSize += bytesLeft;
+ parser.pendingCount = 0;
+
+ } else if ( xmpSize > 0 ) {
+
+ // Pull more of the current buffer into the pending input and try again.
+ // Backup by this pass's overlap so the loop entry code runs OK.
+ parser.pendingCount -= pendingOverlap;
+ buffer -= pendingOverlap;
+ xmpSize += pendingOverlap;
+
+ } else {
+
+ // There is no more of the current buffer. Wait for more. Partial sequences at
+ // the end of the last buffer should be treated as Latin-1 by ProcessUTF8Portion.
+ XMP_Assert ( ! lastClientCall );
+ parser.pendingCount = bytesLeft;
+ memcpy ( &parser.pendingInput[0], &parser.pendingInput[bytesDone], bytesLeft ); // AUDIT: Count is safe.
+ return false; // Wait for the next buffer.
+
+ }
+
+ }
+
+ // Done with the pending input, process the current buffer.
+
+ size_t bytesDone = ProcessUTF8Portion ( xmlParser, (XMP_Uns8*)buffer, xmpSize, lastClientCall );
+
+ #if Trace_ParsingHackery
+ fprintf ( stderr, " ProcessUTF8Portion handled %d additional bytes\n", bytesDone );
+ #endif
+
+ if ( bytesDone < xmpSize ) {
+
+ XMP_Assert ( ! lastClientCall );
+ size_t bytesLeft = xmpSize - bytesDone;
+ if ( bytesLeft > kXMLPendingInputMax ) XMP_Throw ( "Parser bytesLeft too large", kXMPErr_InternalFailure );
+
+ memcpy ( parser.pendingInput, &buffer[bytesDone], bytesLeft ); // AUDIT: Count is safe.
+ parser.pendingCount = bytesLeft;
+ return false; // Wait for the next buffer.
+
+ }
+
+ }
+
+ return true; // This buffer has been processed.
+
+} // ProcessXMLBuffer
+
+
+// -------------------------------------------------------------------------------------------------
+// ProcessXMLTree
+// --------------
+
+void XMPMeta::ProcessXMLTree ( XMP_OptionBits options )
+{
+
+ #if XMP_DebugBuild && DumpXMLParseTree
+ if ( this->xmlParser->parseLog == 0 ) this->xmlParser->parseLog = stdout;
+ DumpXMLTree ( this->xmlParser->parseLog, this->xmlParser->tree, 0 );
+ #endif
+
+ const XML_Node * xmlRoot = FindRootNode ( *this->xmlParser, options );
+
+ if ( xmlRoot != 0 ) {
+
+ this->ProcessRDF ( *xmlRoot, options );
+
+ NormalizeDCArrays ( &this->tree );
+ if ( this->tree.options & kXMP_PropHasAliases ) MoveExplicitAliases ( &this->tree, options, this->errorCallback );
+ TouchUpDataModel ( this, this->errorCallback );
+
+ // Delete empty schema nodes. Do this last, other cleanup can make empty schema.
+ size_t schemaNum = 0;
+ while ( schemaNum < this->tree.children.size() ) {
+ XMP_Node * currSchema = this->tree.children[schemaNum];
+ if ( currSchema->children.size() > 0 ) {
+ ++schemaNum;
+ } else {
+ delete this->tree.children[schemaNum]; // ! Delete the schema node itself.
+ this->tree.children.erase ( this->tree.children.begin() + schemaNum );
+ }
+ }
+
+ }
+
+} // ProcessXMLTree
+
+
+// -------------------------------------------------------------------------------------------------
+// ParseFromBuffer
+// ---------------
+//
+// Although most clients will probably parse everything in one call, we have a buffered API model
+// and need to support even the extreme case of 1 byte at a time parsing. This is considerably
+// complicated by some special cases for 8-bit input. Because of this, the first thing we do is
+// determine whether the input is 8-bit, UTF-16, or UTF-32.
+//
+// Both the 8-bit special cases and the encoding determination are easier to do with 8 bytes or more
+// of input. The XMLParserAdapter class has a pending-input buffer for this. At the start of parsing
+// we (might) try to fill this buffer before determining the input character encoding. After that,
+// we (might) use this buffer with the current input to simplify the logic in Process8BitInput. The
+// "(might)" part means that we don't actually use the pending-input buffer unless we have to. In
+// particular, the common case of single-buffer parsing won't use it.
+
+void
+XMPMeta::ParseFromBuffer ( XMP_StringPtr buffer,
+ XMP_StringLen xmpSize,
+ XMP_OptionBits options )
+{
+ if ( (buffer == 0) && (xmpSize != 0) ) XMP_Throw ( "Null parse buffer", kXMPErr_BadParam );
+ if ( xmpSize == kXMP_UseNullTermination ) xmpSize = strlen ( buffer );
+
+ const bool lastClientCall = ((options & kXMP_ParseMoreBuffers) == 0); // *** Could use FlagIsSet & FlagIsClear macros.
+
+ if ( this->xmlParser == 0 ) {
+ this->tree.ClearNode(); // Make sure the target XMP object is totally empty.
+ if ( (xmpSize == 0) && lastClientCall ) return; // Tolerate empty parse. Expat complains if there are no XML elements.
+ this->xmlParser = XMP_NewExpatAdapter ( ExpatAdapter::kUseGlobalNamespaces );
+ this->xmlParser->SetErrorCallback ( &this->errorCallback );
+ }
+
+ try { // Cleanup the tree and xmlParser if anything fails.
+
+ bool done = this->ProcessXMLBuffer ( buffer, xmpSize, lastClientCall );
+ if ( ! done ) return; // Wait for the next buffer.
+
+ if ( lastClientCall ) {
+ this->ProcessXMLTree ( options );
+ delete this->xmlParser;
+ this->xmlParser = 0;
+ }
+
+ } catch ( ... ) {
+
+ delete this->xmlParser;
+ this->xmlParser = 0;
+ throw;
+
+ }
+
+} // ParseFromBuffer
+
+// =================================================================================================
diff --git a/gpr/source/lib/xmp_core/XMPMeta-Serialize.cpp b/gpr/source/lib/xmp_core/XMPMeta-Serialize.cpp
new file mode 100644
index 0000000..3c46269
--- /dev/null
+++ b/gpr/source/lib/xmp_core/XMPMeta-Serialize.cpp
@@ -0,0 +1,1396 @@
+// =================================================================================================
+// Copyright 2003-2009 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 "public/include/XMP_Environment.h" // ! This must be the first include!
+#include "XMPCore_Impl.hpp"
+
+#include "XMPMeta.hpp"
+
+#include "public/include/XMP_Version.h"
+#include "UnicodeInlines.incl_cpp"
+#include "UnicodeConversions.hpp"
+
+#include "md5.h"
+
+#if XMP_DebugBuild
+ #include <iostream>
+#endif
+
+using namespace std;
+
+#if XMP_WinBuild
+ #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
+
+
+// *** 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
+// =========================
+
+static const char * kPacketHeader = "<?xpacket begin=\"\xEF\xBB\xBF\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>";
+static const char * kPacketTrailer = "<?xpacket end=\"w\"?>"; // ! The w/r is at [size-4].
+
+static const char * kTXMP_SchemaGroup = "XMP_SchemaGroup";
+
+static const char * kRDF_XMPMetaStart = "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\" x:xmptk=\"";
+static const char * kRDF_XMPMetaEnd = "</x:xmpmeta>";
+
+static const char * kRDF_RDFStart = "<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">";
+static const char * kRDF_RDFEnd = "</rdf:RDF>";
+
+static const char * kRDF_SchemaStart = "<rdf:Description rdf:about=";
+static const char * kRDF_SchemaEnd = "</rdf:Description>";
+
+static const char * kRDF_StructStart = "<rdf:Description>";
+static const char * kRDF_StructEnd = "</rdf:Description>";
+
+static const char * kRDF_BagStart = "<rdf:Bag>";
+static const char * kRDF_BagEnd = "</rdf:Bag>";
+
+static const char * kRDF_SeqStart = "<rdf:Seq>";
+static const char * kRDF_SeqEnd = "</rdf:Seq>";
+
+static const char * kRDF_AltStart = "<rdf:Alt>";
+static const char * kRDF_AltEnd = "</rdf:Alt>";
+
+static const char * kRDF_ItemStart = "<rdf:li>";
+static const char * kRDF_ItemEnd = "</rdf:li>";
+
+static const char * kRDF_ValueStart = "<rdf:value>";
+static const char * kRDF_ValueEnd = "</rdf:value>";
+
+
+// =================================================================================================
+// Static Variables
+// ================
+
+
+// =================================================================================================
+// Local Utilities
+// ===============
+
+
+// -------------------------------------------------------------------------------------------------
+// EstimateRDFSize
+// ---------------
+
+// *** Pull the strlen(kXyz) calls into constants.
+
+static size_t
+EstimateRDFSize ( const XMP_Node * currNode, XMP_Index indent, size_t indentLen )
+{
+ size_t outputLen = 2 * (indent*indentLen + currNode->name.size() + 4); // The property element tags.
+
+ if ( ! currNode->qualifiers.empty() ) {
+ // This node has qualifiers, assume it is written using rdf:value and estimate the qualifiers.
+
+ indent += 2; // Everything else is indented inside the rdf:Description element.
+ outputLen += 2 * ((indent-1)*indentLen + strlen(kRDF_StructStart) + 2); // The rdf:Description tags.
+ outputLen += 2 * (indent*indentLen + strlen(kRDF_ValueStart) + 2); // The rdf:value tags.
+
+ for ( size_t qualNum = 0, qualLim = currNode->qualifiers.size(); qualNum < qualLim; ++qualNum ) {
+ const XMP_Node * currQual = currNode->qualifiers[qualNum];
+ outputLen += EstimateRDFSize ( currQual, indent, indentLen );
+ }
+
+ }
+
+ if ( currNode->options & kXMP_PropValueIsStruct ) {
+ indent += 1;
+ outputLen += 2 * (indent*indentLen + strlen(kRDF_StructStart) + 2); // The rdf:Description tags.
+ } else if ( currNode->options & kXMP_PropValueIsArray ) {
+ indent += 2;
+ outputLen += 2 * ((indent-1)*indentLen + strlen(kRDF_BagStart) + 2); // The rdf:Bag/Seq/Alt tags.
+ outputLen += 2 * currNode->children.size() * (strlen(kRDF_ItemStart) + 2); // The rdf:li tags, indent counted in children.
+ } else if ( ! (currNode->options & kXMP_SchemaNode) ) {
+ outputLen += currNode->value.size(); // This is a leaf value node.
+ }
+
+ for ( size_t childNum = 0, childLim = currNode->children.size(); childNum < childLim; ++childNum ) {
+ const XMP_Node * currChild = currNode->children[childNum];
+ outputLen += EstimateRDFSize ( currChild, indent+1, indentLen );
+ }
+
+ return outputLen;
+
+} // EstimateRDFSize
+
+
+// -------------------------------------------------------------------------------------------------
+// DeclareOneNamespace
+// -------------------
+
+static void
+DeclareOneNamespace ( XMP_StringPtr nsPrefix,
+ XMP_StringPtr nsURI,
+ XMP_VarString & usedNS, // ! A catenation of the prefixes with colons.
+ XMP_VarString & outputStr,
+ XMP_StringPtr newline,
+ XMP_StringPtr indentStr,
+ XMP_Index indent )
+{
+ XMP_VarString boundedPrefix = ":";
+ boundedPrefix += nsPrefix;
+ size_t nsPos = usedNS.find ( boundedPrefix );
+
+ if ( nsPos == XMP_VarString::npos ) {
+
+ outputStr += newline;
+ for ( ; indent > 0; --indent ) outputStr += indentStr;
+ outputStr += "xmlns:";
+ outputStr += nsPrefix;
+ outputStr[outputStr.size()-1] = '='; // Change the colon to =.
+ outputStr += '"';
+ outputStr += nsURI;
+ outputStr += '"';
+
+ usedNS += nsPrefix;
+
+ }
+
+} // DeclareOneNamespace
+
+
+// -------------------------------------------------------------------------------------------------
+// DeclareElemNamespace
+// --------------------
+
+static void
+DeclareElemNamespace ( const XMP_VarString & elemName,
+ XMP_VarString & usedNS,
+ XMP_VarString & outputStr,
+ XMP_StringPtr newline,
+ XMP_StringPtr indentStr,
+ XMP_Index indent )
+{
+ size_t colonPos = elemName.find ( ':' );
+
+ if ( colonPos != XMP_VarString::npos ) {
+ XMP_VarString nsPrefix ( elemName.substr ( 0, colonPos+1 ) );
+ XMP_StringPtr nsURI;
+ bool nsFound = sRegisteredNamespaces->GetURI ( nsPrefix.c_str(), &nsURI, 0 );
+ XMP_Enforce ( nsFound );
+ DeclareOneNamespace ( nsPrefix.c_str(), nsURI, usedNS, outputStr, newline, indentStr, indent );
+ }
+
+} // DeclareElemNamespace
+
+
+// -------------------------------------------------------------------------------------------------
+// DeclareUsedNamespaces
+// ---------------------
+
+static void
+DeclareUsedNamespaces ( const XMP_Node * currNode,
+ XMP_VarString & usedNS,
+ XMP_VarString & outputStr,
+ XMP_StringPtr newline,
+ XMP_StringPtr indentStr,
+ XMP_Index indent )
+{
+
+ if ( currNode->options & kXMP_SchemaNode ) {
+ // The schema node name is the URI, the value is the prefix.
+ DeclareOneNamespace ( currNode->value.c_str(), currNode->name.c_str(), usedNS, outputStr, newline, indentStr, indent );
+ } else if ( currNode->options & kXMP_PropValueIsStruct ) {
+ for ( size_t fieldNum = 0, fieldLim = currNode->children.size(); fieldNum < fieldLim; ++fieldNum ) {
+ const XMP_Node * currField = currNode->children[fieldNum];
+ DeclareElemNamespace ( currField->name, usedNS, outputStr, newline, indentStr, indent );
+ }
+ }
+
+ for ( size_t childNum = 0, childLim = currNode->children.size(); childNum < childLim; ++childNum ) {
+ const XMP_Node * currChild = currNode->children[childNum];
+ DeclareUsedNamespaces ( currChild, usedNS, outputStr, newline, indentStr, indent );
+ }
+
+ for ( size_t qualNum = 0, qualLim = currNode->qualifiers.size(); qualNum < qualLim; ++qualNum ) {
+ const XMP_Node * currQual = currNode->qualifiers[qualNum];
+ DeclareElemNamespace ( currQual->name, usedNS, outputStr, newline, indentStr, indent );
+ DeclareUsedNamespaces ( currQual, usedNS, outputStr, newline, indentStr, indent );
+ }
+
+} // DeclareUsedNamespaces
+
+// -------------------------------------------------------------------------------------------------
+// EmitRDFArrayTag
+// ---------------
+
+enum {
+ kIsStartTag = true,
+ kIsEndTag = false
+};
+
+static void
+EmitRDFArrayTag ( XMP_OptionBits arrayForm,
+ XMP_VarString & outputStr,
+ XMP_StringPtr newline,
+ XMP_StringPtr indentStr,
+ XMP_Index indent,
+ XMP_Index arraySize,
+ bool isStartTag )
+{
+ if ( (! isStartTag) && (arraySize == 0) ) return;
+
+ for ( XMP_Index level = indent; level > 0; --level ) outputStr += indentStr;
+ if ( isStartTag ) {
+ outputStr += "<rdf:";
+ } else {
+ outputStr += "</rdf:";
+ }
+
+ if ( arrayForm & kXMP_PropArrayIsAlternate ) {
+ outputStr += "Alt";
+ } else if ( arrayForm & kXMP_PropArrayIsOrdered ) {
+ outputStr += "Seq";
+ } else {
+ outputStr += "Bag";
+ }
+
+ if ( isStartTag && (arraySize == 0) ) outputStr += '/';
+ outputStr += '>';
+ outputStr += newline;
+
+} // EmitRDFArrayTag
+
+
+// -------------------------------------------------------------------------------------------------
+// AppendNodeValue
+// ---------------
+//
+// Append a property or qualifier value to the output with appropriate XML escaping. The escaped
+// characters for elements and attributes are '&', '<', '>', and ASCII controls (tab, LF, CR). In
+// addition, '"' is escaped for attributes. For efficiency, this is done in a double loop. The outer
+// loop makes sure the whole value is processed. The inner loop does a contiguous unescaped run
+// followed by one escaped character (if we're not at the end).
+//
+// We depend on parsing and SetProperty logic to make sure there are no invalid ASCII controls in
+// the XMP values. The XML spec only allows tab, LF, and CR. Others are not even allowed as
+// numeric escape sequences.
+
+enum {
+ kForAttribute = true,
+ kForElement = false
+};
+
+static void
+AppendNodeValue ( XMP_VarString & outputStr, const XMP_VarString & value, bool forAttribute )
+{
+
+ unsigned char * runStart = (unsigned char *) value.c_str();
+ unsigned char * runLimit = runStart + value.size();
+ unsigned char * runEnd;
+ unsigned char ch;
+
+ while ( runStart < runLimit ) {
+
+ for ( runEnd = runStart; runEnd < runLimit; ++runEnd ) {
+ ch = *runEnd;
+ if ( forAttribute && (ch == '"') ) break;
+ if ( (ch < 0x20) || (ch == '&') || (ch == '<') || (ch == '>') ) break;
+ }
+
+ outputStr.append ( (char *) runStart, (runEnd - runStart) );
+
+ if ( runEnd < runLimit ) {
+
+ if ( ch < 0x20 ) {
+
+ XMP_Assert ( (ch == kTab) || (ch == kLF) || (ch == kCR) );
+
+ char hexBuf[16];
+ memcpy ( hexBuf, "&#xn;", 6 ); // AUDIT: Length of "&#xn;" is 5, hexBuf size is 16.
+ hexBuf[3] = kHexDigits[ch&0xF];
+ outputStr.append ( hexBuf, 5 );
+
+ } else {
+
+ if ( ch == '"' ) {
+ outputStr += "&quot;";
+ } else if ( ch == '<' ) {
+ outputStr += "&lt;";
+ } else if ( ch == '>' ) {
+ outputStr += "&gt;";
+ } else {
+ XMP_Assert ( ch == '&' );
+ outputStr += "&amp;";
+ }
+
+ }
+
+ ++runEnd;
+
+ }
+
+ runStart = runEnd;
+
+ }
+
+} // AppendNodeValue
+
+
+// -------------------------------------------------------------------------------------------------
+// CanBeRDFAttrProp
+// ----------------
+
+static bool
+CanBeRDFAttrProp ( const XMP_Node * propNode )
+{
+
+ if ( propNode->name[0] == '[' ) return false;
+ if ( ! propNode->qualifiers.empty() ) return false;
+ if ( propNode->options & kXMP_PropValueIsURI ) return false;
+ if ( propNode->options & kXMP_PropCompositeMask ) return false;
+
+ return true;
+
+} // CanBeRDFAttrProp
+
+
+// -------------------------------------------------------------------------------------------------
+// IsRDFAttrQualifier
+// ------------------
+
+static XMP_StringPtr sAttrQualifiers[] = { "xml:lang", "rdf:resource", "rdf:ID", "rdf:bagID", "rdf:nodeID", "" };
+
+static bool
+IsRDFAttrQualifier ( XMP_VarString qualName )
+{
+
+ for ( size_t i = 0; *sAttrQualifiers[i] != 0; ++i ) {
+ if ( qualName == sAttrQualifiers[i] ) return true;
+ }
+
+ return false;
+
+} // IsRDFAttrQualifier
+
+
+// -------------------------------------------------------------------------------------------------
+// StartOuterRDFDescription
+// ------------------------
+//
+// Start the outer rdf:Description element, including all needed xmlns attributes. Leave the element
+// open so that the compact form can add proprtty attributes.
+
+static void
+StartOuterRDFDescription ( const XMP_Node & xmpTree,
+ XMP_VarString & outputStr,
+ XMP_StringPtr newline,
+ XMP_StringPtr indentStr,
+ XMP_Index baseIndent )
+{
+
+ // Begin the outer rdf:Description start tag.
+
+ for ( XMP_Index level = baseIndent+2; level > 0; --level ) outputStr += indentStr;
+ outputStr += kRDF_SchemaStart;
+ outputStr += '"';
+ outputStr += xmpTree.name;
+ outputStr += '"';
+
+ // Write all necessary xmlns attributes.
+
+ XMP_VarString usedNS;
+ usedNS.reserve ( 400 ); // The predefined prefixes add up to about 320 bytes.
+ usedNS = ":xml:rdf:";
+
+ for ( size_t schema = 0, schemaLim = xmpTree.children.size(); schema != schemaLim; ++schema ) {
+ const XMP_Node * currSchema = xmpTree.children[schema];
+ DeclareUsedNamespaces ( currSchema, usedNS, outputStr, newline, indentStr, baseIndent+4 );
+ }
+
+} // StartOuterRDFDescription
+
+// -------------------------------------------------------------------------------------------------
+// SerializeCanonicalRDFProperty
+// -----------------------------
+//
+// Recursively handles the "value" for a node. It does not matter if it is a top level property, a
+// field of a struct, or an item of an array. The indent is that for the property element. An
+// xml:lang qualifier is written as an attribute of the property start tag, not by itself forcing
+// the qualified property form. The patterns below mostly ignore attribute qualifiers like xml:lang.
+// Except for the one struct case, attribute qualifiers don't affect the output form.
+//
+// <ns:UnqualifiedSimpleProperty>value</ns:UnqualifiedSimpleProperty>
+//
+// <ns:UnqualifiedStructProperty> (If no rdf:resource qualifier)
+// <rdf:Description>
+// ... Fields, same forms as top level properties
+// </rdf:Description>
+// </ns:UnqualifiedStructProperty>
+//
+// <ns:ResourceStructProperty rdf:resource="URI"
+// ... Fields as attributes
+// >
+//
+// <ns:UnqualifiedArrayProperty>
+// <rdf:Bag> or Seq or Alt
+// ... Array items as rdf:li elements, same forms as top level properties
+// </rdf:Bag>
+// </ns:UnqualifiedArrayProperty>
+//
+// <ns:QualifiedProperty>
+// <rdf:Description>
+// <rdf:value> ... Property "value" following the unqualified forms ... </rdf:value>
+// ... Qualifiers looking like named struct fields
+// </rdf:Description>
+// </ns:QualifiedProperty>
+
+enum { kUseCanonicalRDF = true, kUseAdobeVerboseRDF = false };
+enum { kEmitAsRDFValue = true, kEmitAsNormalValue = false };
+
+static void
+SerializeCanonicalRDFProperty ( const XMP_Node * propNode,
+ XMP_VarString & outputStr,
+ XMP_StringPtr newline,
+ XMP_StringPtr indentStr,
+ XMP_Index indent,
+ bool useCanonicalRDF,
+ bool emitAsRDFValue )
+{
+ XMP_Index level;
+ bool emitEndTag = true;
+ bool indentEndTag = true;
+
+ XMP_OptionBits propForm = propNode->options & kXMP_PropCompositeMask;
+
+ // ------------------------------------------------------------------------------------------
+ // Determine the XML element name. Open the start tag with the name and attribute qualifiers.
+
+ XMP_StringPtr elemName = propNode->name.c_str();
+ if ( emitAsRDFValue ) {
+ elemName= "rdf:value";
+ } else if ( *elemName == '[' ) {
+ elemName = "rdf:li";
+ }
+
+ for ( level = indent; level > 0; --level ) outputStr += indentStr;
+ outputStr += '<';
+ outputStr += elemName;
+
+ bool hasGeneralQualifiers = false;
+ bool hasRDFResourceQual = false;
+
+ for ( size_t qualNum = 0, qualLim = propNode->qualifiers.size(); qualNum < qualLim; ++qualNum ) {
+ const XMP_Node * currQual = propNode->qualifiers[qualNum];
+ if ( ! IsRDFAttrQualifier ( currQual->name ) ) {
+ hasGeneralQualifiers = true;
+ } else {
+ if ( currQual->name == "rdf:resource" ) hasRDFResourceQual = true;
+ if ( ! emitAsRDFValue ) {
+ outputStr += ' ';
+ outputStr += currQual->name;
+ outputStr += "=\"";
+ AppendNodeValue ( outputStr, currQual->value, kForAttribute );
+ outputStr += '"';
+ }
+ }
+ }
+
+ // --------------------------------------------------------
+ // Process the property according to the standard patterns.
+
+ if ( hasGeneralQualifiers && (! emitAsRDFValue) ) {
+
+ // -----------------------------------------------------------------------------------------
+ // This node has general, non-attribute, qualifiers. Emit using the qualified property form.
+ // ! The value is output by a recursive call ON THE SAME NODE with emitAsRDFValue set.
+
+ if ( hasRDFResourceQual ) {
+ XMP_Throw ( "Can't mix rdf:resource and general qualifiers", kXMPErr_BadRDF );
+ }
+
+ if ( ! useCanonicalRDF ) {
+ outputStr += " rdf:parseType=\"Resource\">";
+ outputStr += newline;
+ } else {
+ outputStr += '>';
+ outputStr += newline;
+ indent += 1;
+ for ( level = indent; level > 0; --level ) outputStr += indentStr;
+ outputStr += "<rdf:Description>";
+ outputStr += newline;
+ }
+
+ SerializeCanonicalRDFProperty ( propNode, outputStr, newline, indentStr, indent+1,
+ useCanonicalRDF, kEmitAsRDFValue );
+
+ for ( size_t qualNum = 0, qualLim = propNode->qualifiers.size(); qualNum < qualLim; ++qualNum ) {
+ const XMP_Node * currQual = propNode->qualifiers[qualNum];
+ if ( IsRDFAttrQualifier ( currQual->name ) ) continue;
+ SerializeCanonicalRDFProperty ( currQual, outputStr, newline, indentStr, indent+1,
+ useCanonicalRDF, kEmitAsNormalValue );
+ }
+
+ if ( useCanonicalRDF ) {
+ for ( level = indent; level > 0; --level ) outputStr += indentStr;
+ outputStr += "</rdf:Description>";
+ outputStr += newline;
+ indent -= 1;
+ }
+
+ } else {
+
+ // --------------------------------------------------------------------
+ // This node has no general qualifiers. Emit using an unqualified form.
+
+ if ( propForm == 0 ) {
+
+ // --------------------------
+ // This is a simple property.
+
+ if ( propNode->options & kXMP_PropValueIsURI ) {
+ outputStr += " rdf:resource=\"";
+ AppendNodeValue ( outputStr, propNode->value, kForAttribute );
+ outputStr += "\"/>";
+ outputStr += newline;
+ emitEndTag = false;
+ } else if ( propNode->value.empty() ) {
+ outputStr += "/>";
+ outputStr += newline;
+ emitEndTag = false;
+ } else {
+ outputStr += '>';
+ AppendNodeValue ( outputStr, propNode->value, kForElement );
+ indentEndTag = false;
+ }
+
+ } else if ( propForm & kXMP_PropValueIsArray ) {
+
+ // This is an array.
+ outputStr += '>';
+ outputStr += newline;
+ EmitRDFArrayTag ( propForm, outputStr, newline, indentStr, indent+1, propNode->children.size(), kIsStartTag );
+ if ( XMP_ArrayIsAltText(propNode->options) ) NormalizeLangArray ( (XMP_Node*)propNode );
+ for ( size_t childNum = 0, childLim = propNode->children.size(); childNum < childLim; ++childNum ) {
+ const XMP_Node * currChild = propNode->children[childNum];
+ SerializeCanonicalRDFProperty ( currChild, outputStr, newline, indentStr, indent+2,
+ useCanonicalRDF, kEmitAsNormalValue );
+ }
+ EmitRDFArrayTag ( propForm, outputStr, newline, indentStr, indent+1, propNode->children.size(), kIsEndTag );
+
+
+ } else if ( ! hasRDFResourceQual ) {
+
+ // This is a "normal" struct, use the nested field element form form.
+ XMP_Assert ( propForm & kXMP_PropValueIsStruct );
+ if ( propNode->children.size() == 0 ) {
+ if ( ! useCanonicalRDF ) {
+ outputStr += " rdf:parseType=\"Resource\"/>";
+ outputStr += newline;
+ emitEndTag = false;
+ } else {
+ outputStr += '>';
+ outputStr += newline;
+ for ( level = indent+1; level > 0; --level ) outputStr += indentStr;
+ outputStr += "<rdf:Description/>";
+ outputStr += newline;
+ }
+ } else {
+ if ( ! useCanonicalRDF ) {
+ outputStr += " rdf:parseType=\"Resource\">";
+ outputStr += newline;
+ } else {
+ outputStr += '>';
+ outputStr += newline;
+ indent += 1;
+ for ( level = indent; level > 0; --level ) outputStr += indentStr;
+ outputStr += "<rdf:Description>";
+ outputStr += newline;
+ }
+ for ( size_t childNum = 0, childLim = propNode->children.size(); childNum < childLim; ++childNum ) {
+ const XMP_Node * currChild = propNode->children[childNum];
+ SerializeCanonicalRDFProperty ( currChild, outputStr, newline, indentStr, indent+1,
+ useCanonicalRDF, kEmitAsNormalValue );
+ }
+ if ( useCanonicalRDF ) {
+ for ( level = indent; level > 0; --level ) outputStr += indentStr;
+ outputStr += "</rdf:Description>";
+ outputStr += newline;
+ indent -= 1;
+ }
+ }
+
+ } else {
+
+ // This is a struct with an rdf:resource attribute, use the "empty property element" form.
+ XMP_Assert ( propForm & kXMP_PropValueIsStruct );
+ for ( size_t childNum = 0, childLim = propNode->children.size(); childNum < childLim; ++childNum ) {
+ const XMP_Node * currChild = propNode->children[childNum];
+ if ( ! CanBeRDFAttrProp ( currChild ) ) {
+ XMP_Throw ( "Can't mix rdf:resource and complex fields", kXMPErr_BadRDF );
+ }
+ outputStr += newline;
+ for ( level = indent+1; level > 0; --level ) outputStr += indentStr;
+ outputStr += ' ';
+ outputStr += currChild->name;
+ outputStr += "=\"";
+ outputStr += currChild->value;
+ outputStr += '"';
+ }
+ outputStr += "/>";
+ outputStr += newline;
+ emitEndTag = false;
+
+ }
+
+ }
+
+ // ----------------------------------
+ // Emit the property element end tag.
+
+ if ( emitEndTag ) {
+ if ( indentEndTag ) for ( level = indent; level > 0; --level ) outputStr += indentStr;
+ outputStr += "</";
+ outputStr += elemName;
+ outputStr += '>';
+ outputStr += newline;
+ }
+
+} // SerializeCanonicalRDFProperty
+
+
+// -------------------------------------------------------------------------------------------------
+// SerializeCanonicalRDFSchemas
+// ----------------------------
+//
+// Each schema's properties are written to the single rdf:Description element. All of the necessary
+// namespaces are declared in the rdf:Description element. The baseIndent is the base level for the
+// entire serialization, that of the x:xmpmeta element. An xml:lang qualifier is written as an
+// attribute of the property start tag, not by itself forcing the qualified property form.
+//
+// <rdf:Description rdf:about="TreeName"
+// xmlns:ns="URI" ... >
+//
+// ... The actual properties of the schema, see SerializeCanonicalRDFProperty
+//
+// <!-- ns1:Alias is aliased to ns2:Actual --> ... If alias comments are wanted
+//
+// </rdf:Description>
+
+static void
+SerializeCanonicalRDFSchemas ( const XMP_Node & xmpTree,
+ XMP_VarString & outputStr,
+ XMP_StringPtr newline,
+ XMP_StringPtr indentStr,
+ XMP_Index baseIndent,
+ bool useCanonicalRDF )
+{
+
+ StartOuterRDFDescription ( xmpTree, outputStr, newline, indentStr, baseIndent );
+
+ if ( xmpTree.children.size() > 0 ) {
+ outputStr += ">";
+ outputStr += newline;
+ } else {
+ outputStr += "/>";
+ outputStr += newline;
+ return; // ! Done if there are no XMP properties.
+ }
+
+ for ( size_t schemaNum = 0, schemaLim = xmpTree.children.size(); schemaNum < schemaLim; ++schemaNum ) {
+ const XMP_Node * currSchema = xmpTree.children[schemaNum];
+ for ( size_t propNum = 0, propLim = currSchema->children.size(); propNum < propLim; ++propNum ) {
+ const XMP_Node * currProp = currSchema->children[propNum];
+ SerializeCanonicalRDFProperty ( currProp, outputStr, newline, indentStr, baseIndent+3,
+ useCanonicalRDF, kEmitAsNormalValue );
+ }
+ }
+
+ // Write the rdf:Description end tag.
+ for ( XMP_Index level = baseIndent+2; level > 0; --level ) outputStr += indentStr;
+ outputStr += kRDF_SchemaEnd;
+ outputStr += newline;
+
+} // SerializeCanonicalRDFSchemas
+
+
+// -------------------------------------------------------------------------------------------------
+// SerializeCompactRDFAttrProps
+// ----------------------------
+//
+// Write each of the parent's simple unqualified properties as an attribute. Returns true if all
+// of the properties are written as attributes.
+
+static bool
+SerializeCompactRDFAttrProps ( const XMP_Node * parentNode,
+ XMP_VarString & outputStr,
+ XMP_StringPtr newline,
+ XMP_StringPtr indentStr,
+ XMP_Index indent )
+{
+ size_t prop, propLim;
+ bool allAreAttrs = true;
+
+ for ( prop = 0, propLim = parentNode->children.size(); prop != propLim; ++prop ) {
+
+ const XMP_Node * currProp = parentNode->children[prop];
+ if ( ! CanBeRDFAttrProp ( currProp ) ) {
+ allAreAttrs = false;
+ continue;
+ }
+
+ outputStr += newline;
+ for ( XMP_Index level = indent; level > 0; --level ) outputStr += indentStr;
+ outputStr += currProp->name;
+ outputStr += "=\"";
+ AppendNodeValue ( outputStr, currProp->value, kForAttribute );
+ outputStr += '"';
+
+ }
+
+ return allAreAttrs;
+
+} // SerializeCompactRDFAttrProps
+
+
+// -------------------------------------------------------------------------------------------------
+// SerializeCompactRDFElemProps
+// ----------------------------
+//
+// Recursively handles the "value" for a node that must be written as an RDF property element. It
+// does not matter if it is a top level property, a field of a struct, or an item of an array. The
+// indent is that for the property element. The patterns bwlow ignore attribute qualifiers such as
+// xml:lang, they don't affect the output form.
+//
+// <ns:UnqualifiedStructProperty-1
+// ... The fields as attributes, if all are simple and unqualified
+// />
+//
+// <ns:UnqualifiedStructProperty-2 rdf:parseType="Resource">
+// ... The fields as elements, if none are simple and unqualified
+// </ns:UnqualifiedStructProperty-2>
+//
+// <ns:UnqualifiedStructProperty-3>
+// <rdf:Description
+// ... The simple and unqualified fields as attributes
+// >
+// ... The compound or qualified fields as elements
+// </rdf:Description>
+// </ns:UnqualifiedStructProperty-3>
+//
+// <ns:UnqualifiedArrayProperty>
+// <rdf:Bag> or Seq or Alt
+// ... Array items as rdf:li elements, same forms as top level properties
+// </rdf:Bag>
+// </ns:UnqualifiedArrayProperty>
+//
+// <ns:QualifiedProperty rdf:parseType="Resource">
+// <rdf:value> ... Property "value" following the unqualified forms ... </rdf:value>
+// ... Qualifiers looking like named struct fields
+// </ns:QualifiedProperty>
+
+// *** Consider numbered array items, but has compatibility problems.
+// *** Consider qualified form with rdf:Description and attributes.
+
+static void
+SerializeCompactRDFElemProps ( const XMP_Node * parentNode,
+ XMP_VarString & outputStr,
+ XMP_StringPtr newline,
+ XMP_StringPtr indentStr,
+ XMP_Index indent )
+{
+ XMP_Index level;
+
+ for ( size_t prop = 0, propLim = parentNode->children.size(); prop != propLim; ++prop ) {
+
+ const XMP_Node * propNode = parentNode->children[prop];
+ if ( CanBeRDFAttrProp ( propNode ) ) continue;
+
+ bool emitEndTag = true;
+ bool indentEndTag = true;
+
+ XMP_OptionBits propForm = propNode->options & kXMP_PropCompositeMask;
+
+ // -----------------------------------------------------------------------------------
+ // Determine the XML element name, write the name part of the start tag. Look over the
+ // qualifiers to decide on "normal" versus "rdf:value" form. Emit the attribute
+ // qualifiers at the same time.
+
+ XMP_StringPtr elemName = propNode->name.c_str();
+ if ( *elemName == '[' ) elemName = "rdf:li";
+
+ for ( level = indent; level > 0; --level ) outputStr += indentStr;
+ outputStr += '<';
+ outputStr += elemName;
+
+ bool hasGeneralQualifiers = false;
+ bool hasRDFResourceQual = false;
+
+ for ( size_t qualNum = 0, qualLim = propNode->qualifiers.size(); qualNum < qualLim; ++qualNum ) {
+ const XMP_Node * currQual = propNode->qualifiers[qualNum];
+ if ( ! IsRDFAttrQualifier ( currQual->name ) ) {
+ hasGeneralQualifiers = true;
+ } else {
+ if ( currQual->name == "rdf:resource" ) hasRDFResourceQual = true;
+ outputStr += ' ';
+ outputStr += currQual->name;
+ outputStr += "=\"";
+ AppendNodeValue ( outputStr, currQual->value, kForAttribute );
+ outputStr += '"';
+ }
+ }
+
+ // --------------------------------------------------------
+ // Process the property according to the standard patterns.
+
+ if ( hasGeneralQualifiers ) {
+
+ // -------------------------------------------------------------------------------------
+ // The node has general qualifiers, ones that can't be attributes on a property element.
+ // Emit using the qualified property pseudo-struct form. The value is output by a call
+ // to SerializeCanonicalRDFProperty with emitAsRDFValue set.
+
+ // *** We're losing compactness in the calls to SerializeCanonicalRDFProperty.
+ // *** Should refactor to have SerializeCompactRDFProperty that does one node.
+
+ outputStr += " rdf:parseType=\"Resource\">";
+ outputStr += newline;
+
+ SerializeCanonicalRDFProperty ( propNode, outputStr, newline, indentStr, indent+1,
+ kUseAdobeVerboseRDF, kEmitAsRDFValue );
+
+ size_t qualNum = 0;
+ size_t qualLim = propNode->qualifiers.size();
+ if ( propNode->options & kXMP_PropHasLang ) ++qualNum;
+
+ for ( ; qualNum < qualLim; ++qualNum ) {
+ const XMP_Node * currQual = propNode->qualifiers[qualNum];
+ SerializeCanonicalRDFProperty ( currQual, outputStr, newline, indentStr, indent+1,
+ kUseAdobeVerboseRDF, kEmitAsNormalValue );
+ }
+
+ } else {
+
+ // --------------------------------------------------------------------
+ // This node has only attribute qualifiers. Emit as a property element.
+
+ if ( propForm == 0 ) {
+
+ // --------------------------
+ // This is a simple property.
+
+ if ( propNode->options & kXMP_PropValueIsURI ) {
+ outputStr += " rdf:resource=\"";
+ AppendNodeValue ( outputStr, propNode->value, kForAttribute );
+ outputStr += "\"/>";
+ outputStr += newline;
+ emitEndTag = false;
+ } else if ( propNode->value.empty() ) {
+ outputStr += "/>";
+ outputStr += newline;
+ emitEndTag = false;
+ } else {
+ outputStr += '>';
+ AppendNodeValue ( outputStr, propNode->value, kForElement );
+ indentEndTag = false;
+ }
+
+ } else if ( propForm & kXMP_PropValueIsArray ) {
+
+ // -----------------
+ // This is an array.
+
+ outputStr += '>';
+ outputStr += newline;
+ EmitRDFArrayTag ( propForm, outputStr, newline, indentStr, indent+1, propNode->children.size(), kIsStartTag );
+
+ if ( XMP_ArrayIsAltText(propNode->options) ) NormalizeLangArray ( (XMP_Node*)propNode );
+ SerializeCompactRDFElemProps ( propNode, outputStr, newline, indentStr, indent+2 );
+
+ EmitRDFArrayTag ( propForm, outputStr, newline, indentStr, indent+1, propNode->children.size(), kIsEndTag );
+
+ } else {
+
+ // ----------------------
+ // This must be a struct.
+
+ XMP_Assert ( propForm & kXMP_PropValueIsStruct );
+
+ bool hasAttrFields = false;
+ bool hasElemFields = false;
+
+ size_t field, fieldLim;
+ for ( field = 0, fieldLim = propNode->children.size(); field != fieldLim; ++field ) {
+ XMP_Node * currField = propNode->children[field];
+ if ( CanBeRDFAttrProp ( currField ) ) {
+ hasAttrFields = true;
+ if ( hasElemFields ) break; // No sense looking further.
+ } else {
+ hasElemFields = true;
+ if ( hasAttrFields ) break; // No sense looking further.
+ }
+ }
+
+ if ( hasRDFResourceQual && hasElemFields ) {
+ XMP_Throw ( "Can't mix rdf:resource qualifier and element fields", kXMPErr_BadRDF );
+ }
+
+ if ( propNode->children.size() == 0 ) {
+
+ // Catch an empty struct as a special case. The case below would emit an empty
+ // XML element, which gets reparsed as a simple property with an empty value.
+ outputStr += " rdf:parseType=\"Resource\"/>";
+ outputStr += newline;
+ emitEndTag = false;
+
+ } else if ( ! hasElemFields ) {
+
+ // All fields can be attributes, use the emptyPropertyElt form.
+ SerializeCompactRDFAttrProps ( propNode, outputStr, newline, indentStr, indent+1 );
+ outputStr += "/>";
+ outputStr += newline;
+ emitEndTag = false;
+
+ } else if ( ! hasAttrFields ) {
+
+ // All fields must be elements, use the parseTypeResourcePropertyElt form.
+ outputStr += " rdf:parseType=\"Resource\">";
+ outputStr += newline;
+ SerializeCompactRDFElemProps ( propNode, outputStr, newline, indentStr, indent+1 );
+
+ } else {
+
+ // Have a mix of attributes and elements, use an inner rdf:Description.
+ outputStr += '>';
+ outputStr += newline;
+ for ( level = indent+1; level > 0; --level ) outputStr += indentStr;
+ outputStr += "<rdf:Description";
+ SerializeCompactRDFAttrProps ( propNode, outputStr, newline, indentStr, indent+2 );
+ outputStr += ">";
+ outputStr += newline;
+ SerializeCompactRDFElemProps ( propNode, outputStr, newline, indentStr, indent+1 );
+ for ( level = indent+1; level > 0; --level ) outputStr += indentStr;
+ outputStr += kRDF_StructEnd;
+ outputStr += newline;
+
+ }
+
+ }
+
+ }
+
+ // ----------------------------------
+ // Emit the property element end tag.
+
+ if ( emitEndTag ) {
+ if ( indentEndTag ) for ( level = indent; level > 0; --level ) outputStr += indentStr;
+ outputStr += "</";
+ outputStr += elemName;
+ outputStr += '>';
+ outputStr += newline;
+ }
+
+ }
+
+} // SerializeCompactRDFElemProps
+
+
+// -------------------------------------------------------------------------------------------------
+// SerializeCompactRDFSchemas
+// --------------------------
+//
+// All properties from all schema are written in a single rdf:Description element, as are all of the
+// necessary namespace declarations. The baseIndent is the base level for the entire serialization,
+// that of the x:xmpmeta element. The x:xmpmeta and rdf:RDF elements have already been written.
+//
+// Top level simple unqualified properties are written as attributes of the (only) rdf:Description
+// element. Structs, arrays, and qualified properties are written by SerializeCompactRDFElemProp. An
+// xml:lang qualifier on a simple property prevents the attribute form.
+//
+// <rdf:Description rdf:about="TreeName"
+// xmlns:ns="URI" ...
+// ns:UnqualifiedSimpleProperty="value" ... >
+// ... The remaining properties of the schema, see SerializeCompactRDFElemProps
+// </rdf:Description>
+
+static void
+SerializeCompactRDFSchemas ( const XMP_Node & xmpTree,
+ XMP_VarString & outputStr,
+ XMP_StringPtr newline,
+ XMP_StringPtr indentStr,
+ XMP_Index baseIndent )
+{
+ XMP_Index level;
+ size_t schema, schemaLim;
+
+ StartOuterRDFDescription ( xmpTree, outputStr, newline, indentStr, baseIndent );
+
+ // Write the top level "attrProps" and close the rdf:Description start tag.
+ bool allAreAttrs = true;
+ for ( schema = 0, schemaLim = xmpTree.children.size(); schema != schemaLim; ++schema ) {
+ const XMP_Node * currSchema = xmpTree.children[schema];
+ allAreAttrs &= SerializeCompactRDFAttrProps ( currSchema, outputStr, newline, indentStr, baseIndent+3 );
+ }
+ if ( ! allAreAttrs ) {
+ outputStr += ">";
+ outputStr += newline;
+ } else {
+ outputStr += "/>";
+ outputStr += newline;
+ return; // ! Done if all properties in all schema are written as attributes.
+ }
+
+ // Write the remaining properties for each schema.
+ for ( schema = 0, schemaLim = xmpTree.children.size(); schema != schemaLim; ++schema ) {
+ const XMP_Node * currSchema = xmpTree.children[schema];
+ SerializeCompactRDFElemProps ( currSchema, outputStr, newline, indentStr, baseIndent+3 );
+ }
+
+ // Write the rdf:Description end tag.
+ for ( level = baseIndent+2; level > 0; --level ) outputStr += indentStr;
+ outputStr += kRDF_SchemaEnd;
+ outputStr += newline;
+
+} // SerializeCompactRDFSchemas
+
+// -------------------------------------------------------------------------------------------------
+// SerializeAsRDF
+// --------------
+//
+// <?xpacket begin... ?>
+// <x:xmpmeta xmlns:x=... >
+// <rdf:RDF xmlns:rdf=... >
+//
+// ... The properties, see SerializeCanonicalRDFSchema or SerializeCompactRDFSchemas
+//
+// </rdf:RDF>
+// </x:xmpmeta>
+// <?xpacket end... ?>
+
+// *** Need to strip empty arrays?
+// *** Option to strip/keep empty structs?
+// *** Need to verify handling of rdf:type qualifiers in canonical and compact.
+// *** Need to verify round tripping of rdf:ID and similar qualifiers, see RDF 7.2.21.
+// *** Check cases of rdf:resource plus explicit attr qualifiers (like xml:lang).
+
+static void
+SerializeAsRDF ( const XMPMeta & xmpObj,
+ XMP_VarString & headStr, // Everything up to the padding.
+ XMP_VarString & tailStr, // Everything after the padding.
+ XMP_OptionBits options,
+ XMP_StringPtr newline,
+ XMP_StringPtr indentStr,
+ XMP_Index baseIndent )
+{
+ const size_t treeNameLen = xmpObj.tree.name.size();
+ const size_t indentLen = strlen ( indentStr );
+
+ // First estimate the worst case space and reserve room in the output string. This optimization
+ // avoids reallocating and copying the output as it grows. The initial count does not look at
+ // the values of properties, so it does not account for character entities, e.g. &#xA; for newline.
+ // Since there can be a lot of these in things like the base 64 encoding of a large thumbnail,
+ // inflate the count by 1/4 (easy to do) to accommodate.
+
+ // *** Need to include estimate for alias comments.
+
+ size_t outputLen = 2 * (strlen(kPacketHeader) + strlen(kRDF_XMPMetaStart) + strlen(kRDF_RDFStart) + 3*baseIndent*indentLen);
+
+ for ( size_t schemaNum = 0, schemaLim = xmpObj.tree.children.size(); schemaNum < schemaLim; ++schemaNum ) {
+ const XMP_Node * currSchema = xmpObj.tree.children[schemaNum];
+ outputLen += 2*(baseIndent+2)*indentLen + strlen(kRDF_SchemaStart) + treeNameLen + strlen(kRDF_SchemaEnd) + 2;
+ outputLen += EstimateRDFSize ( currSchema, baseIndent+2, indentLen );
+ }
+
+ outputLen += (outputLen >> 2); // Inflate by 1/4, an empirical fudge factor.
+
+ // Now generate the RDF into the head string as UTF-8.
+
+ XMP_Index level;
+
+ std::string rdfstring;
+ headStr.erase();
+ rdfstring.reserve ( outputLen );
+
+ // Write the rdf:RDF start tag.
+ rdfstring += kRDF_RDFStart;
+ rdfstring += newline;
+
+ // Write all of the properties.
+ if ( options & kXMP_UseCompactFormat ) {
+ SerializeCompactRDFSchemas ( xmpObj.tree, rdfstring, newline, indentStr, baseIndent );
+ } else {
+ bool useCanonicalRDF = XMP_OptionIsSet ( options, kXMP_UseCanonicalFormat );
+ SerializeCanonicalRDFSchemas ( xmpObj.tree, rdfstring, newline, indentStr, baseIndent, useCanonicalRDF );
+ }
+
+ // Write the rdf:RDF end tag.
+ for ( level = baseIndent+1; level > 0; --level ) rdfstring += indentStr;
+ rdfstring += kRDF_RDFEnd;
+ // Write the packet header PI.
+ if ( ! (options & kXMP_OmitPacketWrapper) ) {
+ for ( level = baseIndent; level > 0; --level ) headStr += indentStr;
+ headStr += kPacketHeader;
+ headStr += newline;
+ }
+
+ // Write the xmpmeta element's start tag.
+ if ( ! (options & kXMP_OmitXMPMetaElement) ) {
+ for ( level = baseIndent; level > 0; --level ) headStr += indentStr;
+ headStr += kRDF_XMPMetaStart;
+ headStr += kXMPCore_VersionMessage "\"";
+ std::string digestStr;
+ unsigned char digestBin [16];
+ if (options & kXMP_IncludeRDFHash)
+ {
+ std::string hashrdf;
+
+ {
+ context_md5_t ctx;
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, (unsigned char*)rdfstring.c_str(), (unsigned int)rdfstring.size() );
+ MD5Final(digestBin, &ctx);
+ }
+
+ char buffer [40];
+ for ( int in = 0, out = 0; in < 16; in += 1, out += 2 ) {
+ XMP_Uns8 byte = digestBin[in];
+ buffer[out] = kHexDigits [ byte >> 4 ];
+ buffer[out+1] = kHexDigits [ byte & 0xF ];
+ }
+ buffer[32] = 0;
+ digestStr.append ( buffer );
+ headStr += " rdfhash=\"";
+ headStr += digestStr + "\"";
+ headStr += " merged=\"0\"";
+ }
+ headStr += ">";
+ headStr += newline;
+ }
+
+ for ( level = baseIndent+1; level > 0; --level ) headStr += indentStr;
+ headStr+= rdfstring ;
+ headStr += newline;
+
+ // Write the xmpmeta end tag.
+ if ( ! (options & kXMP_OmitXMPMetaElement) ) {
+ for ( level = baseIndent; level > 0; --level ) headStr += indentStr;
+ headStr += kRDF_XMPMetaEnd;
+ headStr += newline;
+ }
+
+ // Write the packet trailer PI into the tail string as UTF-8.
+ tailStr.erase();
+ if ( ! (options & kXMP_OmitPacketWrapper) ) {
+ tailStr.reserve ( strlen(kPacketTrailer) + (strlen(indentStr) * baseIndent) );
+ for ( level = baseIndent; level > 0; --level ) tailStr += indentStr;
+ tailStr += kPacketTrailer;
+ if ( options & kXMP_ReadOnlyPacket ) tailStr[tailStr.size()-4] = 'r';
+ }
+
+} // SerializeAsRDF
+
+
+// -------------------------------------------------------------------------------------------------
+// SerializeToBuffer
+// -----------------
+
+void
+XMPMeta::SerializeToBuffer ( XMP_VarString * rdfString,
+ XMP_OptionBits options,
+ XMP_StringLen padding,
+ XMP_StringPtr newline,
+ XMP_StringPtr indentStr,
+ XMP_Index baseIndent ) const
+{
+ XMP_Enforce( rdfString != 0 );
+ XMP_Assert ( (newline != 0) && (indentStr != 0) );
+ rdfString->erase();
+
+ // Fix up some default parameters.
+
+ enum { kDefaultPad = 2048 };
+ size_t unicodeUnitSize = 1;
+ XMP_OptionBits charEncoding = options & kXMP_EncodingMask;
+
+ if ( charEncoding != kXMP_EncodeUTF8 ) {
+ if ( options & _XMP_UTF16_Bit ) {
+ if ( options & _XMP_UTF32_Bit ) XMP_Throw ( "Can't use both _XMP_UTF16_Bit and _XMP_UTF32_Bit", kXMPErr_BadOptions );
+ unicodeUnitSize = 2;
+ } else if ( options & _XMP_UTF32_Bit ) {
+ unicodeUnitSize = 4;
+ } else {
+ XMP_Throw ( "Can't use _XMP_LittleEndian_Bit by itself", kXMPErr_BadOptions );
+ }
+ }
+
+ if ( options & kXMP_OmitAllFormatting ) {
+ newline = " "; // ! Yes, a space for "newline". This ensures token separation.
+ indentStr = "";
+ } else {
+ if ( *newline == 0 ) newline = "\xA"; // Linefeed
+ if ( *indentStr == 0 ) {
+ indentStr = " ";
+ if ( ! (options & kXMP_UseCompactFormat) ) indentStr = " ";
+ }
+ }
+
+ if ( options & kXMP_ExactPacketLength ) {
+ if ( options & (kXMP_OmitPacketWrapper | kXMP_IncludeThumbnailPad) ) {
+ XMP_Throw ( "Inconsistent options for exact size serialize", kXMPErr_BadOptions );
+ }
+ if ( (padding & (unicodeUnitSize-1)) != 0 ) {
+ XMP_Throw ( "Exact size must be a multiple of the Unicode element", kXMPErr_BadOptions );
+ }
+ } else if ( options & kXMP_ReadOnlyPacket ) {
+ if ( options & (kXMP_OmitPacketWrapper | kXMP_IncludeThumbnailPad) ) {
+ XMP_Throw ( "Inconsistent options for read-only packet", kXMPErr_BadOptions );
+ }
+ padding = 0;
+ } else if ( options & kXMP_OmitPacketWrapper ) {
+ if ( options & kXMP_IncludeThumbnailPad ) {
+ XMP_Throw ( "Inconsistent options for non-packet serialize", kXMPErr_BadOptions );
+ }
+ padding = 0;
+ } else if ( options & kXMP_OmitXMPMetaElement ) {
+ if ( options & kXMP_IncludeRDFHash ) {
+ XMP_Throw ( "Inconsistent options for x:xmpmeta serialize", kXMPErr_BadOptions );
+ }
+ padding = 0;
+ } else {
+ if ( padding == 0 ) {
+ padding = kDefaultPad * unicodeUnitSize;
+ } else if ( (padding >> 28) != 0 ) {
+ XMP_Throw ( "Outrageously large padding size", kXMPErr_BadOptions ); // Bigger than 256 MB.
+ }
+ if ( options & kXMP_IncludeThumbnailPad ) {
+ if ( ! this->DoesPropertyExist ( kXMP_NS_XMP, "Thumbnails" ) ) padding += (10000 * unicodeUnitSize); // *** Need a better estimate.
+ }
+ }
+
+ // Serialize as UTF-8, then convert to UTF-16 or UTF-32 if necessary, and assemble with the padding and tail.
+
+ std::string tailStr;
+
+ SerializeAsRDF ( *this, *rdfString, tailStr, options, newline, indentStr, baseIndent );
+
+ if ( charEncoding == kXMP_EncodeUTF8 ) {
+
+ if ( options & kXMP_ExactPacketLength ) {
+ size_t minSize = rdfString->size() + tailStr.size();
+ if ( minSize > padding ) XMP_Throw ( "Can't fit into specified packet size", kXMPErr_BadSerialize );
+ padding -= minSize; // Now the actual amount of padding to add.
+ }
+
+ size_t newlineLen = strlen ( newline );
+
+ if ( padding < newlineLen ) {
+ rdfString->append ( padding, ' ' );
+ } else {
+ padding -= newlineLen; // Write this newline last.
+ while ( padding >= (100 + newlineLen) ) {
+ rdfString->append ( 100, ' ' );
+ *rdfString += newline;
+ padding -= (100 + newlineLen);
+ }
+ rdfString->append ( padding, ' ' );
+ *rdfString += newline;
+ }
+
+ *rdfString += tailStr;
+
+ } else {
+
+ // Need to convert the encoding. Swap the UTF-8 into a local string and convert back. Assemble everything.
+
+ XMP_VarString utf8Str, newlineStr;
+ bool bigEndian = ((charEncoding & _XMP_LittleEndian_Bit) == 0);
+
+ if ( charEncoding & _XMP_UTF16_Bit ) {
+
+ std::string padStr ( " " ); padStr[0] = 0; // Assume big endian.
+
+ utf8Str.swap ( *rdfString );
+ ToUTF16 ( (UTF8Unit*)utf8Str.c_str(), utf8Str.size(), rdfString, bigEndian );
+ utf8Str.swap ( tailStr );
+ ToUTF16 ( (UTF8Unit*)utf8Str.c_str(), utf8Str.size(), &tailStr, bigEndian );
+
+ if ( options & kXMP_ExactPacketLength ) {
+ size_t minSize = rdfString->size() + tailStr.size();
+ if ( minSize > padding ) XMP_Throw ( "Can't fit into specified packet size", kXMPErr_BadSerialize );
+ padding -= minSize; // Now the actual amount of padding to add (in bytes).
+ }
+
+ utf8Str.assign ( newline );
+ ToUTF16 ( (UTF8Unit*)utf8Str.c_str(), utf8Str.size(), &newlineStr, bigEndian );
+ size_t newlineLen = newlineStr.size();
+
+ if ( padding < newlineLen ) {
+ for ( int i = padding/2; i > 0; --i ) *rdfString += padStr;
+ } else {
+ padding -= newlineLen; // Write this newline last.
+ while ( padding >= (200 + newlineLen) ) {
+ for ( int i = 100; i > 0; --i ) *rdfString += padStr;
+ *rdfString += newlineStr;
+ padding -= (200 + newlineLen);
+ }
+ for ( int i = padding/2; i > 0; --i ) *rdfString += padStr;
+ *rdfString += newlineStr;
+ }
+
+ *rdfString += tailStr;
+
+ } else {
+
+ std::string padStr ( " " ); padStr[0] = padStr[1] = padStr[2] = 0; // Assume big endian.
+ UTF8_to_UTF32_Proc Converter = UTF8_to_UTF32BE;
+
+ if ( charEncoding & _XMP_LittleEndian_Bit ) {
+ padStr[0] = ' '; padStr[1] = padStr[2] = padStr[3] = 0;
+ Converter = UTF8_to_UTF32LE;
+ }
+
+ utf8Str.swap ( *rdfString );
+ ToUTF32 ( (UTF8Unit*)utf8Str.c_str(), utf8Str.size(), rdfString, bigEndian );
+ utf8Str.swap ( tailStr );
+ ToUTF32 ( (UTF8Unit*)utf8Str.c_str(), utf8Str.size(), &tailStr, bigEndian );
+
+ if ( options & kXMP_ExactPacketLength ) {
+ size_t minSize = rdfString->size() + tailStr.size();
+ if ( minSize > padding ) XMP_Throw ( "Can't fit into specified packet size", kXMPErr_BadSerialize );
+ padding -= minSize; // Now the actual amount of padding to add (in bytes).
+ }
+
+ utf8Str.assign ( newline );
+ ToUTF32 ( (UTF8Unit*)utf8Str.c_str(), utf8Str.size(), &newlineStr, bigEndian );
+ size_t newlineLen = newlineStr.size();
+
+ if ( padding < newlineLen ) {
+ for ( int i = padding/4; i > 0; --i ) *rdfString += padStr;
+ } else {
+ padding -= newlineLen; // Write this newline last.
+ while ( padding >= (400 + newlineLen) ) {
+ for ( int i = 100; i > 0; --i ) *rdfString += padStr;
+ *rdfString += newlineStr;
+ padding -= (400 + newlineLen);
+ }
+ for ( int i = padding/4; i > 0; --i ) *rdfString += padStr;
+ *rdfString += newlineStr;
+ }
+
+ *rdfString += tailStr;
+
+ }
+
+ }
+
+} // SerializeToBuffer
+
+// =================================================================================================
diff --git a/gpr/source/lib/xmp_core/XMPMeta.cpp b/gpr/source/lib/xmp_core/XMPMeta.cpp
new file mode 100644
index 0000000..62bcb60
--- /dev/null
+++ b/gpr/source/lib/xmp_core/XMPMeta.cpp
@@ -0,0 +1,1381 @@
+// =================================================================================================
+// Copyright 2003 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 <algorithm> // For sort and stable_sort.
+
+#include "public/include/XMP_Environment.h" // ! This must be the first include!
+#include "XMPCore_Impl.hpp"
+
+#include "XMPMeta.hpp"
+#include "XMPIterator.hpp"
+#include "XMPUtils.hpp"
+#include "public/include/XMP_Version.h"
+#include "UnicodeInlines.incl_cpp"
+#include "UnicodeConversions.hpp"
+
+#include <cstdio> // For snprintf.
+
+#if XMP_DebugBuild
+ #include <iostream>
+#endif
+
+using namespace std;
+
+#if XMP_WinBuild
+ #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)
+ #pragma warning ( disable : 4996 ) // '...' was declared deprecated
+#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
+// =========================
+
+
+// =================================================================================================
+// Static Variables
+// ================
+
+XMP_VarString * xdefaultName = 0; // Needed in XMPMeta-Parse.cpp, MoveExplicitAliases.
+
+static XMPMeta::ErrorCallbackInfo sDefaultErrorCallback;
+
+// These are embedded version strings.
+
+const char * kXMPCore_EmbeddedVersion = kXMPCore_VersionMessage;
+const char * kXMPCore_EmbeddedCopyright = kXMPCoreName " " kXMP_CopyrightStr;
+
+// =================================================================================================
+// Local Utilities
+// ===============
+
+
+// -------------------------------------------------------------------------------------------------
+// DumpNodeOptions
+// ---------------
+
+static void
+DumpNodeOptions ( XMP_OptionBits options,
+ XMP_TextOutputProc outProc,
+ void * refCon )
+{
+ char buffer [32]; // Decimal of a 64 bit int is at most about 20 digits.
+ memset(buffer, 0, 32);
+
+ static const char * optNames[] = { " schema", // 0x8000_0000
+ " ?30",
+ " ?29",
+ " -COMMAS-",
+ " ?27", // 0x0800_0000
+ " ?26",
+ " ?25",
+ " ?24",
+ " ?23", // 0x0080_0000
+ " isStale",
+ " isDerived",
+ " isStable",
+ " ?19", // 0x0008_0000
+ " isInternal",
+ " hasAliases",
+ " isAlias",
+ " -AFTER-", // 0x0000_8000
+ " -BEFORE-",
+ " isCompact",
+ " isLangAlt",
+ " isAlt", // 0x0000_0800
+ " isOrdered",
+ " isArray",
+ " isStruct",
+ " hasType", // 0x0000_0080
+ " hasLang",
+ " isQual",
+ " hasQual",
+ " ?3", // 0x0000_0008
+ " ?2",
+ " URI",
+ " ?0" };
+
+ if ( options == 0 ) {
+
+ OutProcNChars ( "(0x0)", 5 );
+
+ } else {
+
+ OutProcNChars ( "(0x", 3 );
+ OutProcHexInt ( options );
+ OutProcNChars ( " :", 2 );
+
+ XMP_OptionBits mask = 0x80000000;
+ for ( int b = 0; b < 32; ++b ) {
+ if ( options & mask ) OutProcLiteral ( optNames[b] );
+ mask = mask >> 1;
+ }
+ OutProcNChars ( ")", 1 );
+
+ }
+
+} // DumpNodeOptions
+
+
+// -------------------------------------------------------------------------------------------------
+// DumpPropertyTree
+// ----------------
+
+// *** Extract the validation code into a separate routine to call on exit in debug builds.
+
+static void
+DumpPropertyTree ( const XMP_Node * currNode,
+ int indent,
+ size_t itemIndex,
+ XMP_TextOutputProc outProc,
+ void * refCon )
+{
+ char buffer [32]; // Decimal of a 64 bit int is at most about 20 digits.
+
+ OutProcIndent ( (size_t)indent );
+ if ( itemIndex == 0 ) {
+ if ( currNode->options & kXMP_PropIsQualifier ) OutProcNChars ( "? ", 2 );
+ DumpClearString ( currNode->name, outProc, refCon );
+ } else {
+ OutProcNChars ( "[", 1 );
+ OutProcDecInt ( itemIndex );
+ OutProcNChars ( "]", 1 );
+ }
+
+ if ( ! (currNode->options & kXMP_PropCompositeMask) ) {
+ OutProcNChars ( " = \"", 4 );
+ DumpClearString ( currNode->value, outProc, refCon );
+ OutProcNChars ( "\"", 1 );
+ }
+
+ if ( currNode->options != 0 ) {
+ OutProcNChars ( " ", 2 );
+ DumpNodeOptions ( currNode->options, outProc, refCon );
+ }
+
+ if ( currNode->options & kXMP_PropHasLang ) {
+ if ( currNode->qualifiers.empty() || (currNode->qualifiers[0]->name != "xml:lang") ) {
+ OutProcLiteral ( " ** bad lang flag **" );
+ }
+ }
+ // *** Check rdf:type also.
+
+ if ( ! (currNode->options & kXMP_PropCompositeMask) ) {
+ if ( ! currNode->children.empty() ) OutProcLiteral ( " ** bad children **" );
+ } else if ( currNode->options & kXMP_PropValueIsArray ) {
+ if ( currNode->options & kXMP_PropValueIsStruct ) OutProcLiteral ( " ** bad comp flags **" );
+ } else if ( (currNode->options & kXMP_PropCompositeMask) != kXMP_PropValueIsStruct ) {
+ OutProcLiteral ( " ** bad comp flags **" );
+ }
+
+ #if 0 // *** XMP_DebugBuild
+ if ( (currNode->_namePtr != currNode->name.c_str()) ||
+ (currNode->_valuePtr != currNode->value.c_str()) ) OutProcLiteral ( " ** bad debug string **" );
+ #endif
+
+ OutProcNewline();
+
+ for ( size_t qualNum = 0, qualLim = currNode->qualifiers.size(); qualNum < qualLim; ++qualNum ) {
+
+ const XMP_Node * currQual = currNode->qualifiers[qualNum];
+
+ if ( currQual->parent != currNode ) OutProcLiteral ( "** bad parent link => " );
+ if ( currQual->name == kXMP_ArrayItemName ) OutProcLiteral ( "** bad qual name => " );
+ if ( ! (currQual->options & kXMP_PropIsQualifier) ) OutProcLiteral ( "** bad qual flag => " );
+ if ( currQual->name == "xml:lang" ) {
+ if ( (qualNum != 0) || (! (currNode->options & kXMP_PropHasLang)) ) OutProcLiteral ( "** bad lang qual => " );
+ }
+
+ DumpPropertyTree ( currQual, indent+2, 0, outProc, refCon );
+
+ }
+
+ for ( size_t childNum = 0, childLim = currNode->children.size(); childNum < childLim; ++childNum ) {
+
+ const XMP_Node * currChild = currNode->children[childNum];
+
+ if ( currChild->parent != currNode ) OutProcLiteral ( "** bad parent link => " );
+ if ( currChild->options & kXMP_PropIsQualifier ) OutProcLiteral ( "** bad qual flag => " );
+
+ if ( currNode->options & kXMP_PropValueIsArray ) {
+ itemIndex = childNum+1;
+ if ( currChild->name != kXMP_ArrayItemName ) OutProcLiteral ( "** bad item name => " );
+ } else {
+ itemIndex = 0;
+ if ( currChild->name == kXMP_ArrayItemName ) OutProcLiteral ( "** bad field name => " );
+ }
+
+ DumpPropertyTree ( currChild, indent+1, itemIndex, outProc, refCon );
+
+ }
+
+} // DumpPropertyTree
+
+
+// -------------------------------------------------------------------------------------------------
+// DumpXMLTree
+// -----------
+
+#if DumpXMLParseTree
+
+static inline void PutHexByte ( FILE * log, unsigned char ch )
+{
+
+ fprintf ( log, "\\x" );
+ if ( ch < 0x10 ) {
+ fprintf ( log, "%c", kHexDigits[ch] );
+ } else {
+ fprintf ( log, "%c%c", kHexDigits[ch>>4], kHexDigits[ch&0xF] );
+ }
+
+} // PutHexByte
+
+// -------------------------------------------------------------------------------------------------
+
+static void PutClearString ( FILE * log, const std::string & str )
+{
+
+ for ( size_t i = 0; i != str.size(); ++i ) {
+ unsigned char ch = str[i];
+ if ( (0x20 <= ch) && (ch <= 0x7F) ) {
+ fprintf ( log, "%c", ch );
+ } else {
+ PutHexByte ( log, ch );
+ }
+ }
+
+} // PutClearString
+
+// -------------------------------------------------------------------------------------------------
+
+static void DumpXMLTree ( FILE * log, const XML_Node & node, int indent )
+{
+ size_t i;
+
+ #if 0 // *** XMP_DebugBuild
+ if ( (node._namePtr != node.name.c_str()) ||
+ (node._valuePtr != node.value.c_str()) ) fprintf ( log, "*** bad debug string ***\n" );
+ #endif
+
+ for ( i = 0; i != (size_t)indent; ++i ) fprintf ( log, " " );
+
+ switch ( node.kind ) {
+
+ case kRootNode :
+ fprintf ( log, "\nStart of XML tree dump\n\n" );
+ if ( (indent != 0) || (! node.attrs.empty()) ||
+ (! node.ns.empty()) || (! node.name.empty()) || (!node.value.empty()) ) fprintf ( log, " ** invalid root ** \n" );
+ for ( i = 0; i < node.children.size(); ++i ) {
+ XMP_Uns8 kind = node.children[i]->kind;
+ if ( (kind == kRootNode) || (kind == kAttrNode) ) fprintf ( log, " ** invalid child ** \n" );
+ DumpXMLTree ( log, *node.children[i], indent+1 );
+ }
+ fprintf ( log, "\nEnd of XML tree dump\n" );
+ break;
+
+ case kElemNode :
+ fprintf ( log, "Elem %s", node.name.c_str() );
+ if ( indent == 0 ) fprintf ( log, " ** invalid elem ** " );
+ if ( ! node.ns.empty() ) fprintf ( log, " @ %s", node.ns.c_str() );
+ fprintf ( log, "\n" );
+ for ( i = 0; i < node.attrs.size(); ++i ) {
+ XMP_Uns8 kind = node.attrs[i]->kind;
+ if ( kind != kAttrNode ) fprintf ( log, " ** invalid attr ** \n" );
+ DumpXMLTree ( log, *node.attrs[i], indent+2 );
+ }
+ for ( i = 0; i < node.children.size(); ++i ) {
+ XMP_Uns8 kind = node.children[i]->kind;
+ if ( (kind == kRootNode) || (kind == kAttrNode) ) fprintf ( log, " ** invalid child ** \n" );
+ DumpXMLTree ( log, *node.children[i], indent+1 );
+ }
+ break;
+
+ case kAttrNode :
+ fprintf ( log, "Attr %s", node.name.c_str() );
+ if ( (indent == 0) || node.name.empty() || (! node.attrs.empty()) || (! node.children.empty()) ) fprintf ( log, " ** invalid attr ** " );
+ fprintf ( log, " = \"" );
+ PutClearString ( log, node.value );
+ fprintf ( log, "\"" );
+ if ( ! node.ns.empty() ) fprintf ( log, " @ %s", node.ns.c_str() );
+ fprintf ( log, "\n" );
+ break;
+
+ case kCDataNode :
+ if ( (indent == 0) || (! node.ns.empty()) || (! node.name.empty()) ||
+ (! node.attrs.empty()) || (! node.children.empty()) ) fprintf ( log, " ** invalid cdata ** \n" );
+ fprintf ( log, "\"" );
+ PutClearString ( log, node.value );
+ fprintf ( log, "\"\n" );
+ break;
+
+ case kPINode :
+ fprintf ( log, "PI %s", node.name.c_str() );
+ if ( (indent == 0) || node.name.empty() || (! node.children.empty()) ) fprintf ( log, " ** invalid pi ** \n" );
+ if ( ! node.value.empty() ) {
+ fprintf ( log, " <? " );
+ PutClearString ( log, node.value );
+ fprintf ( log, " ?>" );
+ }
+ fprintf ( log, "\n" );
+ break;
+
+ }
+
+} // DumpXMLTree
+
+#endif // DumpXMLParseTree
+
+
+// -------------------------------------------------------------------------------------------------
+// CompareNodeNames
+// ----------------
+//
+// Comparison routine for sorting XMP nodes by name. The name "xml:lang" is less than anything else,
+// and "rdf:type" is less than anything except "xml:lang". This preserves special rules for qualifiers.
+
+static bool
+CompareNodeNames ( XMP_Node * left, XMP_Node * right )
+{
+
+ if ( left->name == "xml:lang" ) return true;
+ if ( right->name == "xml:lang" ) return false;
+
+ if ( left->name == "rdf:type" ) return true;
+ if ( right->name == "rdf:type" ) return false;
+
+ return ( left->name < right->name );
+
+} // CompareNodeNames
+
+
+// -------------------------------------------------------------------------------------------------
+// CompareNodeValues
+// -----------------
+//
+// Comparison routine for sorting XMP nodes by value.
+
+static bool
+CompareNodeValues ( XMP_Node * left, XMP_Node * right )
+{
+
+ if ( XMP_PropIsSimple ( left->options ) && XMP_PropIsSimple ( right->options ) ) {
+ return ( left->value < right->value );
+ }
+
+ XMP_OptionBits leftForm = left->options & kXMP_PropCompositeMask;
+ XMP_OptionBits rightForm = right->options & kXMP_PropCompositeMask;
+
+ return ( leftForm < rightForm );
+
+} // CompareNodeValues
+
+
+// -------------------------------------------------------------------------------------------------
+// CompareNodeLangs
+// ----------------
+//
+// Comparison routine for sorting XMP nodes by xml:lang qualifier. An "x-default" value is less than
+// any other language.
+
+static bool
+CompareNodeLangs ( XMP_Node * left, XMP_Node * right )
+{
+
+ if ( left->qualifiers.empty() || (left->qualifiers[0]->name != "xml:lang") ) return false;
+ if ( right->qualifiers.empty() || (right->qualifiers[0]->name != "xml:lang") ) return false;
+
+ if ( left->qualifiers[0]->value == "x-default" ) return true;
+ if ( right->qualifiers[0]->value == "x-default" ) return false;
+
+ return ( left->qualifiers[0]->value < right->qualifiers[0]->value );
+
+} // CompareNodeLangs
+
+
+// -------------------------------------------------------------------------------------------------
+// SortWithinOffspring
+// -------------------
+//
+// Sort one level down, within the elements of a node vector. This sorts the qualifiers of each
+// node. If the node is a struct it sorts the fields by names. If the node is an unordered array it
+// sorts the elements by value. If the node is an AltText array it sorts the elements by language.
+
+static void
+SortWithinOffspring ( XMP_NodeOffspring & nodeVec )
+{
+
+ for ( size_t i = 0, limit = nodeVec.size(); i < limit; ++i ) {
+
+ XMP_Node * currPos = nodeVec[i];
+
+ if ( ! currPos->qualifiers.empty() ) {
+ sort ( currPos->qualifiers.begin(), currPos->qualifiers.end(), CompareNodeNames );
+ SortWithinOffspring ( currPos->qualifiers );
+ }
+
+ if ( ! currPos->children.empty() ) {
+
+ if ( XMP_PropIsStruct ( currPos->options ) || XMP_NodeIsSchema ( currPos->options ) ) {
+ sort ( currPos->children.begin(), currPos->children.end(), CompareNodeNames );
+ } else if ( XMP_PropIsArray ( currPos->options ) ) {
+ if ( XMP_ArrayIsUnordered ( currPos->options ) ) {
+ stable_sort ( currPos->children.begin(), currPos->children.end(), CompareNodeValues );
+ } else if ( XMP_ArrayIsAltText ( currPos->options ) ) {
+ sort ( currPos->children.begin(), currPos->children.end(), CompareNodeLangs );
+ }
+ }
+
+ SortWithinOffspring ( currPos->children );
+
+ }
+
+ }
+
+} // SortWithinOffspring
+
+
+// -------------------------------------------------------------------------------------------------
+// RegisterAlias
+// -------------
+//
+// Allow 3 kinds of alias:
+// TopProp => TopProp
+// TopProp => TopArray[1]
+// TopProp => TopArray[@xml:lang='x-default']
+//
+// A new alias can be made to something that is already aliased, as long as the net result is one of
+// the legitimate forms. The new alias can already have aliases to it, also as long as result of
+// adjusting all of the exiting aliases leaves them legal.
+//
+// ! The caller assumes all risk that new aliases do not invalidate existing XMPMeta objects. Any
+// ! conflicts will result in later references throwing bad XPath exceptions.
+
+static void
+RegisterAlias ( XMP_StringPtr aliasNS,
+ XMP_StringPtr aliasProp,
+ XMP_StringPtr actualNS,
+ XMP_StringPtr actualProp,
+ XMP_OptionBits arrayForm )
+{
+ XMP_ExpandedXPath expAlias, expActual;
+ XMP_AliasMapPos mapPos;
+ XMP_ExpandedXPath * regActual = 0;
+
+ XMP_Assert ( (aliasNS != 0) && (aliasProp != 0) && (actualNS != 0) && (actualProp != 0) ); // Enforced by wrapper.
+
+ // Expand the alias and actual names, make sure they are one of the basic 3 forms. When counting
+ // the expanded XPath size remember that the schema URI is the first component. We don't have to
+ // compare the schema URIs though, the (unique) prefix is part of the top property name.
+
+ ExpandXPath ( aliasNS, aliasProp, &expAlias );
+ ExpandXPath ( actualNS, actualProp, &expActual );
+ if ( (expAlias.size() != 2) || (expActual.size() != 2) ) {
+ XMP_Throw ( "Alias and actual property names must be simple", kXMPErr_BadXPath );
+ }
+
+ arrayForm = VerifySetOptions ( arrayForm, 0 );
+ if ( arrayForm != 0 ) {
+ if ( (arrayForm & ~kXMP_PropArrayFormMask) != 0 ) XMP_Throw ( "Only array form flags are allowed", kXMPErr_BadOptions );
+ expActual[1].options |= arrayForm; // Set the array form for the top level step.
+ if ( ! (arrayForm & kXMP_PropArrayIsAltText) ) {
+ expActual.push_back ( XPathStepInfo ( "[1]", kXMP_ArrayIndexStep ) );
+ } else {
+ expActual.push_back ( XPathStepInfo ( "[?xml:lang=\"x-default\"]", kXMP_QualSelectorStep ) );
+ }
+ }
+
+ // See if there are any conflicts with existing aliases. A couple of the checks are easy. If the
+ // alias is already aliased it is only OK to reregister an identical alias. If the actual is
+ // already aliased to something else and the new chain is legal, just swap in the old base.
+
+ mapPos = sRegisteredAliasMap->find ( expAlias[kRootPropStep].step );
+ if ( mapPos != sRegisteredAliasMap->end() ) {
+
+ // This alias is already registered to something, make sure it is the same something.
+
+ regActual = &mapPos->second;
+ if ( arrayForm != (mapPos->second[1].options & kXMP_PropArrayFormMask) ) {
+ XMP_Throw ( "Mismatch with existing alias array form", kXMPErr_BadParam );
+ }
+ if ( expActual.size() != regActual->size() ) {
+ XMP_Throw ( "Mismatch with existing actual path", kXMPErr_BadParam );
+ }
+ if ( expActual[kRootPropStep].step != (*regActual)[kRootPropStep].step ) {
+ XMP_Throw ( "Mismatch with existing actual name", kXMPErr_BadParam );
+ }
+ if ( (expActual.size() == 3) && (expActual[kAliasIndexStep].step != (*regActual)[kAliasIndexStep].step) ) {
+ XMP_Throw ( "Mismatch with existing actual array item", kXMPErr_BadParam );
+ }
+ return;
+
+ }
+
+ mapPos = sRegisteredAliasMap->find ( expActual[kRootPropStep].step );
+ if ( mapPos != sRegisteredAliasMap->end() ) {
+
+ // The actual is already aliased to something else.
+
+ regActual = &mapPos->second;
+ if ( expActual.size() == 2 ) {
+ expActual = *regActual; // TopProp => TopProp => anything : substitute the entire old base.
+ } else if ( regActual->size() != 2 ) {
+ XMP_Throw ( "Can't alias an array item to an array item", kXMPErr_BadParam ); // TopProp => TopArray[] => TopArray[] : nope.
+ } else {
+ expActual[kSchemaStep].step = (*regActual)[kSchemaStep].step; // TopProp => TopArray[] => TopProp :
+ expActual[kRootPropStep].step = (*regActual)[kRootPropStep].step; // substitute the old base name.
+ }
+
+ }
+
+ // Checking for existing aliases to this one is touchier. This involves updating the alias map,
+ // which must not be done unless all of the changes are legal. So we need 2 loops, one to verify
+ // that everything is OK, and one to make the changes. The bad case is:
+ // TopProp => TopArray[] => TopArray[]
+ // In the valid cases we back substitute the new base.
+
+ for ( mapPos = sRegisteredAliasMap->begin(); mapPos != sRegisteredAliasMap->end(); ++mapPos ) {
+ regActual = &mapPos->second;
+ if ( expAlias[kRootPropStep].step == (*regActual)[kRootPropStep].step ) {
+ if ( (regActual->size() == 2) && (expAlias.size() == 2) ) {
+ XMP_Throw ( "Can't alias an array item to an array item", kXMPErr_BadParam );
+ }
+ }
+ }
+
+ for ( mapPos = sRegisteredAliasMap->begin(); mapPos != sRegisteredAliasMap->end(); ++mapPos ) {
+ regActual = &mapPos->second;
+ if ( expAlias[kRootPropStep].step == (*regActual)[kRootPropStep].step ) {
+
+ if ( regActual->size() == 1 ) {
+ *regActual = expActual; // TopProp => TopProp => anything : substitute the entire new base.
+ } else {
+ (*regActual)[kSchemaStep].step = expActual[kSchemaStep].step; // TopProp => TopArray[] => TopProp :
+ (*regActual)[kRootPropStep].step = expActual[kRootPropStep].step; // substitute the new base name.
+ }
+
+ }
+ }
+
+ // Finally, all is OK to register the new alias.
+
+ (void) sRegisteredAliasMap->insert ( XMP_AliasMap::value_type ( expAlias[kRootPropStep].step, expActual ) );
+
+} // RegisterAlias
+
+
+// -------------------------------------------------------------------------------------------------
+// RegisterStandardAliases
+// -----------------------
+
+static void
+RegisterStandardAliases()
+{
+
+ // Aliases from XMP to DC.
+ RegisterAlias ( kXMP_NS_XMP, "Author", kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered );
+ RegisterAlias ( kXMP_NS_XMP, "Authors", kXMP_NS_DC, "creator", 0 );
+ RegisterAlias ( kXMP_NS_XMP, "Description", kXMP_NS_DC, "description", 0 );
+ RegisterAlias ( kXMP_NS_XMP, "Format", kXMP_NS_DC, "format", 0 );
+ RegisterAlias ( kXMP_NS_XMP, "Keywords", kXMP_NS_DC, "subject", 0 );
+ RegisterAlias ( kXMP_NS_XMP, "Locale", kXMP_NS_DC, "language", 0 );
+ RegisterAlias ( kXMP_NS_XMP, "Title", kXMP_NS_DC, "title", 0 );
+ RegisterAlias ( kXMP_NS_XMP_Rights, "Copyright", kXMP_NS_DC, "rights", 0 );
+
+ // Aliases from PDF to DC and XMP.
+ RegisterAlias ( kXMP_NS_PDF, "Author", kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered );
+ RegisterAlias ( kXMP_NS_PDF, "BaseURL", kXMP_NS_XMP, "BaseURL", 0 );
+ RegisterAlias ( kXMP_NS_PDF, "CreationDate", kXMP_NS_XMP, "CreateDate", 0 );
+ RegisterAlias ( kXMP_NS_PDF, "Creator", kXMP_NS_XMP, "CreatorTool", 0 );
+ RegisterAlias ( kXMP_NS_PDF, "ModDate", kXMP_NS_XMP, "ModifyDate", 0 );
+ RegisterAlias ( kXMP_NS_PDF, "Subject", kXMP_NS_DC, "description", kXMP_PropArrayIsAltText );
+ RegisterAlias ( kXMP_NS_PDF, "Title", kXMP_NS_DC, "title", kXMP_PropArrayIsAltText );
+
+ // Aliases from Photoshop to DC and XMP.
+ RegisterAlias ( kXMP_NS_Photoshop, "Author", kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered );
+ RegisterAlias ( kXMP_NS_Photoshop, "Caption", kXMP_NS_DC, "description", kXMP_PropArrayIsAltText );
+ RegisterAlias ( kXMP_NS_Photoshop, "Copyright", kXMP_NS_DC, "rights", kXMP_PropArrayIsAltText );
+ RegisterAlias ( kXMP_NS_Photoshop, "Keywords", kXMP_NS_DC, "subject", 0 );
+ RegisterAlias ( kXMP_NS_Photoshop, "Marked", kXMP_NS_XMP_Rights, "Marked", 0 );
+ RegisterAlias ( kXMP_NS_Photoshop, "Title", kXMP_NS_DC, "title", kXMP_PropArrayIsAltText );
+ RegisterAlias ( kXMP_NS_Photoshop, "WebStatement", kXMP_NS_XMP_Rights, "WebStatement", 0 );
+
+ // Aliases from TIFF and EXIF to DC and XMP.
+ RegisterAlias ( kXMP_NS_TIFF, "Artist", kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered);
+ RegisterAlias ( kXMP_NS_TIFF, "Copyright", kXMP_NS_DC, "rights", 0 );
+ RegisterAlias ( kXMP_NS_TIFF, "DateTime", kXMP_NS_XMP, "ModifyDate", 0 );
+ RegisterAlias ( kXMP_NS_EXIF, "DateTimeDigitized", kXMP_NS_XMP, "CreateDate", 0 );
+ RegisterAlias ( kXMP_NS_TIFF, "ImageDescription", kXMP_NS_DC, "description", 0 );
+ RegisterAlias ( kXMP_NS_TIFF, "Software", kXMP_NS_XMP, "CreatorTool", 0 );
+
+ // Aliases from PNG to DC and XMP.
+ RegisterAlias ( kXMP_NS_PNG, "Author", kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered);
+ RegisterAlias ( kXMP_NS_PNG, "Copyright", kXMP_NS_DC, "rights", kXMP_PropArrayIsAltText);
+ RegisterAlias ( kXMP_NS_PNG, "CreationTime", kXMP_NS_XMP, "CreateDate", 0 );
+ RegisterAlias ( kXMP_NS_PNG, "Description", kXMP_NS_DC, "description", kXMP_PropArrayIsAltText);
+ RegisterAlias ( kXMP_NS_PNG, "ModificationTime", kXMP_NS_XMP, "ModifyDate", 0 );
+ RegisterAlias ( kXMP_NS_PNG, "Software", kXMP_NS_XMP, "CreatorTool", 0 );
+ RegisterAlias ( kXMP_NS_PNG, "Title", kXMP_NS_DC, "title", kXMP_PropArrayIsAltText);
+
+} // RegisterStandardAliases
+
+
+// =================================================================================================
+// Constructors
+// ============
+
+
+XMPMeta::XMPMeta() : tree(XMP_Node(0,"",0)), clientRefs(0), xmlParser(0)
+{
+ #if XMP_TraceCTorDTor
+ printf ( "Default construct XMPMeta @ %.8X\n", this );
+ #endif
+
+ if ( sDefaultErrorCallback.clientProc != 0 ) {
+ this->errorCallback.wrapperProc = sDefaultErrorCallback.wrapperProc;
+ this->errorCallback.clientProc = sDefaultErrorCallback.clientProc;
+ this->errorCallback.context = sDefaultErrorCallback.context;
+ this->errorCallback.limit = sDefaultErrorCallback.limit;
+ }
+
+} // XMPMeta
+
+// -------------------------------------------------------------------------------------------------
+
+XMPMeta::~XMPMeta() RELEASE_NO_THROW
+{
+ #if XMP_TraceCTorDTor
+ printf ( "Destruct XMPMeta @ %.8X\n", this );
+ #endif
+
+ XMP_Assert ( this->clientRefs <= 0 );
+ if ( xmlParser != 0 ) delete ( xmlParser );
+ xmlParser = 0;
+
+} // ~XMPMeta
+
+
+// =================================================================================================
+// Class Static Functions
+// ======================
+//
+//
+// =================================================================================================
+
+// -------------------------------------------------------------------------------------------------
+// GetVersionInfo
+// --------------
+
+/* class-static */ void
+XMPMeta::GetVersionInfo ( XMP_VersionInfo * info )
+{
+
+ memset ( info, 0, sizeof(*info) ); // AUDIT: Safe, using sizeof the destination.
+ XMP_Assert ( sizeof(*info) == sizeof(XMP_VersionInfo) );
+
+ info->major = XMPCORE_API_VERSION_MAJOR;
+ info->minor = XMPCORE_API_VERSION_MINOR;
+ info->micro = 0; //no longer used
+ info->isDebug = kXMPCore_DebugFlag;
+ info->flags = 0; // ! None defined yet.
+ info->message = kXMPCore_VersionMessage;
+
+} // GetVersionInfo
+
+// -------------------------------------------------------------------------------------------------
+// Initialize
+// ----------
+
+#if XMP_TraceCoreCalls
+ FILE * xmpCoreLog = stderr;
+#endif
+
+#if UseGlobalLibraryLock
+ XMP_BasicMutex sLibraryLock;
+#endif
+
+/* class-static */ bool
+XMPMeta::Initialize()
+{
+ // Allocate and initialize static objects.
+
+ ++sXMP_InitCount;
+ if ( sXMP_InitCount > 1 ) return true;
+
+ #if XMP_TraceCoreCallsToFile
+ xmpCoreLog = fopen ( "XMPCoreLog.txt", "w" );
+ if ( xmpCoreLog == 0 ) xmpCoreLog = stderr;
+ #endif
+
+ #if UseGlobalLibraryLock
+ InitializeBasicMutex ( sLibraryLock );
+ #endif
+
+ if ( ! Initialize_LibUtils() ) return false;
+ xdefaultName = new XMP_VarString ( "x-default" );
+
+ sRegisteredNamespaces = new XMP_NamespaceTable;
+ sRegisteredAliasMap = new XMP_AliasMap;
+
+ InitializeUnicodeConversions();
+
+
+ // Register standard namespaces and aliases.
+
+ XMP_StringPtr voidPtr;
+ XMP_StringLen voidLen;
+
+ (void) RegisterNamespace ( kXMP_NS_XML, "xml", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_RDF, "rdf", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_DC, "dc", &voidPtr, &voidLen );
+
+ (void) RegisterNamespace ( kXMP_NS_XMP, "xmp", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_PDF, "pdf", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_Photoshop, "photoshop", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_PSAlbum, "album", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_EXIF, "exif", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_EXIF_Aux, "aux", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_ExifEX, "exifEX", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_TIFF, "tiff", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_PNG, "png", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_JPEG, "jpeg", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_JP2K, "jp2k", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_CameraRaw, "crs", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_ASF, "asf", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_WAV, "wav", &voidPtr, &voidLen );
+
+ (void) RegisterNamespace ( kXMP_NS_AdobeStockPhoto, "bmsp", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_CreatorAtom, "creatorAtom", &voidPtr, &voidLen );
+
+ (void) RegisterNamespace ( kXMP_NS_XMP_Rights, "xmpRights", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_XMP_MM, "xmpMM", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_XMP_BJ, "xmpBJ", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_XMP_Note, "xmpNote", &voidPtr, &voidLen );
+
+ (void) RegisterNamespace ( kXMP_NS_DM, "xmpDM", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_Script, "xmpScript", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_BWF, "bext", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_AEScart, "AEScart", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_RIFFINFO, "riffinfo", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_XMP_Text, "xmpT", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_XMP_PagedFile, "xmpTPg", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_XMP_Graphics, "xmpG", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_XMP_Image, "xmpGImg", &voidPtr, &voidLen );
+
+ (void) RegisterNamespace ( kXMP_NS_XMP_Font, "stFnt", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_XMP_Dimensions, "stDim", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_XMP_ResourceEvent, "stEvt", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_XMP_ResourceRef, "stRef", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_XMP_ST_Version, "stVer", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_XMP_ST_Job, "stJob", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_XMP_ManifestItem, "stMfs", &voidPtr, &voidLen );
+
+ (void) RegisterNamespace ( kXMP_NS_XMP_IdentifierQual, "xmpidq", &voidPtr, &voidLen );
+
+ (void) RegisterNamespace ( kXMP_NS_IPTCCore, "Iptc4xmpCore", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_IPTCExt, "Iptc4xmpExt", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_DICOM, "DICOM", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_PLUS, "plus", &voidPtr, &voidLen );
+
+ (void) RegisterNamespace ( kXMP_NS_PDFA_Schema, "pdfaSchema", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_PDFA_Property, "pdfaProperty", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_PDFA_Type, "pdfaType", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_PDFA_Field, "pdfaField", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_PDFA_ID, "pdfaid", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_PDFA_Extension, "pdfaExtension", &voidPtr, &voidLen );
+
+ (void) RegisterNamespace ( kXMP_NS_PDFX, "pdfx", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_PDFX_ID, "pdfxid", &voidPtr, &voidLen );
+
+ (void) RegisterNamespace ( "adobe:ns:meta/", "x", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( "http://ns.adobe.com/iX/1.0/", "iX", &voidPtr, &voidLen );
+
+ RegisterStandardAliases();
+
+ // Initialize the other core classes.
+
+ if ( ! XMPIterator::Initialize() ) XMP_Throw ( "Failure from XMPIterator::Initialize", kXMPErr_InternalFailure );
+ if ( ! XMPUtils::Initialize() ) XMP_Throw ( "Failure from XMPUtils::Initialize", kXMPErr_InternalFailure );
+ // Do miscelaneous semantic checks of types and arithmetic.
+
+ XMP_Assert ( sizeof(XMP_Int8) == 1 );
+ XMP_Assert ( sizeof(XMP_Int16) == 2 );
+ XMP_Assert ( sizeof(XMP_Int32) == 4 );
+ XMP_Assert ( sizeof(XMP_Int64) == 8 );
+ XMP_Assert ( sizeof(XMP_Uns8) == 1 );
+ XMP_Assert ( sizeof(XMP_Uns16) == 2 );
+ XMP_Assert ( sizeof(XMP_Uns32) == 4 );
+ XMP_Assert ( sizeof(XMP_Uns64) == 8 );
+ XMP_Assert ( sizeof(XMP_Bool) == 1 );
+
+ XMP_Assert ( sizeof(XMP_OptionBits) == 4 ); // Check that option masking work on all 32 bits.
+ XMP_OptionBits flag = (XMP_OptionBits) (~0UL);
+ XMP_Assert ( flag == (XMP_OptionBits)(-1L) );
+ XMP_Assert ( (flag ^ kXMP_PropHasLang) == 0xFFFFFFBFUL );
+ XMP_Assert ( (flag & ~kXMP_PropHasLang) == 0xFFFFFFBFUL );
+
+ XMP_OptionBits opt1 = 0; // Check the general option bit macros.
+ XMP_OptionBits opt2 = (XMP_OptionBits)~0UL;
+ XMP_SetOption ( opt1, kXMP_PropValueIsArray );
+ XMP_ClearOption ( opt2, kXMP_PropValueIsArray );
+ XMP_Assert ( opt1 == ~opt2 );
+ XMP_Assert ( XMP_TestOption ( opt1, kXMP_PropValueIsArray ) );
+ XMP_Assert ( ! XMP_TestOption ( opt2, kXMP_PropValueIsArray ) );
+
+ XMP_Assert ( XMP_PropIsSimple ( ~kXMP_PropCompositeMask ) ); // Check the special option bit macros.
+ XMP_Assert ( ! XMP_PropIsSimple ( kXMP_PropValueIsStruct ) );
+ XMP_Assert ( ! XMP_PropIsSimple ( kXMP_PropValueIsArray ) );
+
+ XMP_Assert ( XMP_PropIsStruct ( kXMP_PropValueIsStruct ) );
+ XMP_Assert ( XMP_PropIsArray ( kXMP_PropValueIsArray ) );
+ XMP_Assert ( ! XMP_PropIsStruct ( ~kXMP_PropValueIsStruct ) );
+ XMP_Assert ( ! XMP_PropIsArray ( ~kXMP_PropValueIsArray ) );
+
+ XMP_Assert ( XMP_ArrayIsUnordered ( ~kXMP_PropArrayIsOrdered ) );
+ XMP_Assert ( XMP_ArrayIsOrdered ( kXMP_PropArrayIsOrdered ) );
+ XMP_Assert ( XMP_ArrayIsAlternate ( kXMP_PropArrayIsAlternate ) );
+ XMP_Assert ( XMP_ArrayIsAltText ( kXMP_PropArrayIsAltText ) );
+ XMP_Assert ( ! XMP_ArrayIsUnordered ( kXMP_PropArrayIsOrdered ) );
+ XMP_Assert ( ! XMP_ArrayIsOrdered ( ~kXMP_PropArrayIsOrdered ) );
+ XMP_Assert ( ! XMP_ArrayIsAlternate ( ~kXMP_PropArrayIsAlternate ) );
+ XMP_Assert ( ! XMP_ArrayIsAltText ( ~kXMP_PropArrayIsAltText ) );
+
+ XMP_Assert ( XMP_PropHasQualifiers ( kXMP_PropHasQualifiers ) );
+ XMP_Assert ( XMP_PropIsQualifier ( kXMP_PropIsQualifier ) );
+ XMP_Assert ( XMP_PropHasLang ( kXMP_PropHasLang ) );
+ XMP_Assert ( ! XMP_PropHasQualifiers ( ~kXMP_PropHasQualifiers ) );
+ XMP_Assert ( ! XMP_PropIsQualifier ( ~kXMP_PropIsQualifier ) );
+ XMP_Assert ( ! XMP_PropHasLang ( ~kXMP_PropHasLang ) );
+
+ XMP_Assert ( XMP_NodeIsSchema ( kXMP_SchemaNode ) );
+ XMP_Assert ( XMP_PropIsAlias ( kXMP_PropIsAlias ) );
+ XMP_Assert ( ! XMP_NodeIsSchema ( ~kXMP_SchemaNode ) );
+ XMP_Assert ( ! XMP_PropIsAlias ( ~kXMP_PropIsAlias ) );
+
+ #if 0 // Generally off, enable to hand check generated code.
+ extern XMP_OptionBits opt3, opt4;
+ if ( XMP_TestOption ( opt3, kXMP_PropValueIsArray ) ) opt4 = opt3;
+ if ( ! XMP_TestOption ( opt3, kXMP_PropValueIsStruct ) ) opt4 = opt3;
+ static bool ok1 = XMP_TestOption ( opt4, kXMP_PropValueIsArray );
+ static bool ok2 = ! XMP_TestOption ( opt4, kXMP_PropValueIsStruct );
+ #endif
+
+ // Make sure the embedded info strings are referenced and kept.
+ if ( (kXMPCore_EmbeddedVersion[0] == 0) || (kXMPCore_EmbeddedCopyright[0] == 0) ) return false;
+ return true;
+
+} // Initialize
+
+
+// -------------------------------------------------------------------------------------------------
+// Terminate
+// ---------
+
+/* class-static */ void
+XMPMeta::Terminate() RELEASE_NO_THROW
+{
+ --sXMP_InitCount;
+ if ( sXMP_InitCount != 0 ) return; // Not ready to terminate, or already terminated.
+
+ XMPIterator::Terminate();
+ XMPUtils::Terminate();
+#if ENABLE_NEW_DOM_MODEL
+ NS_XMPCOMMON::ITSingleton< NS_INT_XMPCORE::IXMPCoreObjectFactory >::DestroyInstance();
+ NS_INT_XMPCOMMON::TerminateXMPCommonFramework();
+#endif
+
+ EliminateGlobal ( sRegisteredNamespaces );
+ EliminateGlobal ( sRegisteredAliasMap );
+
+ EliminateGlobal ( xdefaultName );
+
+ Terminate_LibUtils();
+
+ #if UseGlobalLibraryLock
+ TerminateBasicMutex ( sLibraryLock );
+ #endif
+
+ #if XMP_TraceCoreCallsToFile
+ if ( xmpCoreLog != stderr ) fclose ( xmpCoreLog );
+ xmpCoreLog = stderr;
+ #endif
+
+ // reset static variables
+ sDefaultErrorCallback.Clear();
+} // Terminate
+
+
+// -------------------------------------------------------------------------------------------------
+// DumpNamespaces
+// --------------
+//
+// Dump the prefix to URI map (easier to read) and verify that both are consistent and legit.
+
+// *** Should put checks in a separate routine for regular calling in debug builds.
+
+/* class-static */ XMP_Status
+XMPMeta::DumpNamespaces ( XMP_TextOutputProc outProc,
+ void * refCon )
+{
+
+ sRegisteredNamespaces->Dump ( outProc, refCon );
+ return 0;
+
+} // DumpNamespaces
+
+
+// -------------------------------------------------------------------------------------------------
+// GetGlobalOptions
+// ----------------
+
+/* class-static */ XMP_OptionBits
+XMPMeta::GetGlobalOptions()
+{
+ XMP_OptionBits options = 0;
+
+ return options;
+
+} // GetGlobalOptions
+
+
+// -------------------------------------------------------------------------------------------------
+// SetGlobalOptions
+// ----------------
+
+/* class-static */ void
+XMPMeta::SetGlobalOptions ( XMP_OptionBits options )
+{
+
+ XMP_Throw ( "Unimplemented method XMPMeta::SetGlobalOptions", kXMPErr_Unimplemented );
+ void * p; p = &options; // Avoid unused param warnings.
+
+} // SetGlobalOptions
+
+
+// -------------------------------------------------------------------------------------------------
+// RegisterNamespace
+// -----------------
+
+/* class-static */ bool
+XMPMeta::RegisterNamespace ( XMP_StringPtr namespaceURI,
+ XMP_StringPtr suggestedPrefix,
+ XMP_StringPtr * registeredPrefix,
+ XMP_StringLen * prefixSize )
+{
+
+ return sRegisteredNamespaces->Define ( namespaceURI, suggestedPrefix, registeredPrefix, prefixSize );
+
+} // RegisterNamespace
+
+
+// -------------------------------------------------------------------------------------------------
+// GetNamespacePrefix
+// ------------------
+
+/* class-static */ bool
+XMPMeta::GetNamespacePrefix ( XMP_StringPtr namespaceURI,
+ XMP_StringPtr * namespacePrefix,
+ XMP_StringLen * prefixSize )
+{
+
+ return sRegisteredNamespaces->GetPrefix ( namespaceURI, namespacePrefix, prefixSize );
+
+} // GetNamespacePrefix
+
+
+// -------------------------------------------------------------------------------------------------
+// GetNamespaceURI
+// ---------------
+
+/* class-static */ bool
+XMPMeta::GetNamespaceURI ( XMP_StringPtr namespacePrefix,
+ XMP_StringPtr * namespaceURI,
+ XMP_StringLen * uriSize )
+{
+
+ return sRegisteredNamespaces->GetURI ( namespacePrefix, namespaceURI, uriSize );
+
+} // GetNamespaceURI
+
+
+// -------------------------------------------------------------------------------------------------
+// DeleteNamespace
+// ---------------
+
+// *** Don't allow standard namespaces to be deleted.
+// *** We would be better off not having this. Instead, have local namespaces from parsing be
+// *** restricted to the object that introduced them.
+
+/* class-static */ void
+XMPMeta::DeleteNamespace ( XMP_StringPtr namespaceURI )
+{
+
+ XMP_Throw ( "Unimplemented method XMPMeta::DeleteNamespace", kXMPErr_Unimplemented );
+
+} // DeleteNamespace
+
+
+// =================================================================================================
+// Class Methods
+// =============
+//
+//
+// =================================================================================================
+
+
+// -------------------------------------------------------------------------------------------------
+// DumpObject
+// ----------
+
+void
+XMPMeta::DumpObject ( XMP_TextOutputProc outProc,
+ void * refCon ) const
+{
+ XMP_Assert ( outProc != 0 ); // ! Enforced by wrapper.
+
+ OutProcLiteral ( "Dumping XMPMeta object \"" );
+ DumpClearString ( tree.name, outProc, refCon );
+ OutProcNChars ( "\" ", 3 );
+ DumpNodeOptions ( tree.options, outProc, refCon );
+ #if 0 // *** XMP_DebugBuild
+ if ( (tree._namePtr != tree.name.c_str()) ||
+ (tree._valuePtr != tree.value.c_str()) ) OutProcLiteral ( " ** bad debug string **" );
+ #endif
+ OutProcNewline();
+
+ if ( ! tree.value.empty() ) {
+ OutProcLiteral ( "** bad root value ** \"" );
+ DumpClearString ( tree.value, outProc, refCon );
+ OutProcNChars ( "\"", 1 );
+ OutProcNewline();
+ }
+
+ if ( ! tree.qualifiers.empty() ) {
+ OutProcLiteral ( "** bad root qualifiers **" );
+ OutProcNewline();
+ for ( size_t qualNum = 0, qualLim = tree.qualifiers.size(); qualNum < qualLim; ++qualNum ) {
+ DumpPropertyTree ( tree.qualifiers[qualNum], 3, 0, outProc, refCon );
+ }
+ }
+
+ if ( ! tree.children.empty() ) {
+
+ for ( size_t childNum = 0, childLim = tree.children.size(); childNum < childLim; ++childNum ) {
+
+ const XMP_Node * currSchema = tree.children[childNum];
+
+ OutProcNewline();
+ OutProcIndent ( 1 );
+ DumpClearString ( currSchema->value, outProc, refCon );
+ OutProcNChars ( " ", 2 );
+ DumpClearString ( currSchema->name, outProc, refCon );
+ OutProcNChars ( " ", 2 );
+ DumpNodeOptions ( currSchema->options, outProc, refCon );
+ #if 0 // *** XMP_DebugBuild
+ if ( (currSchema->_namePtr != currSchema->name.c_str()) ||
+ (currSchema->_valuePtr != currSchema->value.c_str()) ) OutProcLiteral ( " ** bad debug string **" );
+ #endif
+ OutProcNewline();
+
+ if ( ! (currSchema->options & kXMP_SchemaNode) ) {
+ OutProcLiteral ( "** bad schema options **" );
+ OutProcNewline();
+ }
+
+ if ( ! currSchema->qualifiers.empty() ) {
+ OutProcLiteral ( "** bad schema qualifiers **" );
+ OutProcNewline();
+ for ( size_t qualNum = 0, qualLim = currSchema->qualifiers.size(); qualNum < qualLim; ++qualNum ) {
+ DumpPropertyTree ( currSchema->qualifiers[qualNum], 3, 0, outProc, refCon );
+ }
+ }
+
+ for ( size_t numChild = 0, childLimit = currSchema->children.size(); numChild < childLimit; ++numChild ) {
+ DumpPropertyTree ( currSchema->children[numChild], 2, 0, outProc, refCon );
+ }
+
+ }
+
+ }
+
+} // DumpObject
+
+
+// -------------------------------------------------------------------------------------------------
+// CountArrayItems
+// ---------------
+
+XMP_Index
+XMPMeta::CountArrayItems ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName ) const
+{
+ XMP_Assert ( (schemaNS != 0) && (arrayName != 0) ); // Enforced by wrapper.
+
+ XMP_ExpandedXPath expPath;
+ ExpandXPath ( schemaNS, arrayName, &expPath );
+
+ const XMP_Node * arrayNode = FindConstNode ( &tree, expPath );
+
+ if ( arrayNode == 0 ) return 0;
+ if ( ! (arrayNode->options & kXMP_PropValueIsArray) ) XMP_Throw ( "The named property is not an array", kXMPErr_BadXPath );
+ return arrayNode->children.size();
+
+} // CountArrayItems
+
+
+// -------------------------------------------------------------------------------------------------
+// GetObjectName
+// -------------
+
+void
+XMPMeta::GetObjectName ( XMP_StringPtr * namePtr,
+ XMP_StringLen * nameLen ) const
+{
+
+ *namePtr = tree.name.c_str();
+ *nameLen = tree.name.size();
+
+} // GetObjectName
+
+
+// -------------------------------------------------------------------------------------------------
+// SetObjectName
+// -------------
+
+void
+XMPMeta::SetObjectName ( XMP_StringPtr name )
+{
+ VerifyUTF8 ( name ); // Throws if the string is not legit UTF-8.
+ tree.name = name;
+
+} // SetObjectName
+
+
+// -------------------------------------------------------------------------------------------------
+// GetObjectOptions
+// ----------------
+
+XMP_OptionBits
+XMPMeta::GetObjectOptions() const
+{
+ XMP_OptionBits options = 0;
+
+ return options;
+
+} // GetObjectOptions
+
+
+// -------------------------------------------------------------------------------------------------
+// SetObjectOptions
+// ----------------
+
+void
+XMPMeta::SetObjectOptions ( XMP_OptionBits options )
+{
+
+ XMP_Throw ( "Unimplemented method XMPMeta::SetObjectOptions", kXMPErr_Unimplemented );
+ void * p; p = &options; // Avoid unused param warnings.
+
+} // SetObjectOptions
+
+
+// -------------------------------------------------------------------------------------------------
+// Sort
+// ----
+//
+// At the top level the namespaces are sorted by their prefixes. Within a namespace, the top level
+// properties are sorted by name. Within a struct, the fields are sorted by their qualified name,
+// i.e. their XML prefix:local form. Unordered arrays of simple items are sorted by value. Language
+// Alternative arrays are sorted by the xml:lang qualifiers, with the "x-default" item placed first.
+
+void
+XMPMeta::Sort()
+{
+
+ if ( ! this->tree.qualifiers.empty() ) {
+ sort ( this->tree.qualifiers.begin(), this->tree.qualifiers.end(), CompareNodeNames );
+ SortWithinOffspring ( this->tree.qualifiers );
+ }
+
+ if ( ! this->tree.children.empty() ) {
+ // The schema prefixes are the node's value, the name is the URI, so we sort schemas by value.
+ sort ( this->tree.children.begin(), this->tree.children.end(), CompareNodeValues );
+ SortWithinOffspring ( this->tree.children );
+ }
+
+} // Sort
+
+
+// -------------------------------------------------------------------------------------------------
+// Erase
+// -----
+//
+// Clear everything except for clientRefs.
+
+void
+XMPMeta::Erase()
+{
+
+ if ( this->xmlParser != 0 ) {
+ delete ( this->xmlParser );
+ this->xmlParser = 0;
+ }
+ this->tree.ClearNode();
+
+} // Erase
+
+
+// -------------------------------------------------------------------------------------------------
+// Clone
+// -----
+
+void
+XMPMeta::Clone ( XMPMeta * clone, XMP_OptionBits options ) const
+{
+ if ( clone == 0 ) XMP_Throw ( "Null clone pointer", kXMPErr_BadParam );
+ if ( options != 0 ) XMP_Throw ( "No options are defined yet", kXMPErr_BadOptions );
+ XMP_Assert ( this->tree.parent == 0 );
+
+ clone->tree.ClearNode();
+
+ clone->tree.options = this->tree.options;
+ clone->tree.name = this->tree.name;
+ clone->tree.value = this->tree.value;
+ clone->errorCallback = this->errorCallback;
+
+ #if 0 // *** XMP_DebugBuild
+ clone->tree._namePtr = clone->tree.name.c_str();
+ clone->tree._valuePtr = clone->tree.value.c_str();
+ #endif
+
+ CloneOffspring ( &this->tree, &clone->tree );
+
+} // Clone
+
+// =================================================================================================
+// XMP_Node::GetLocalURI
+// =====================
+//
+// This has to be someplace where XMPMeta::GetNamespaceURI is visible.
+
+void XMP_Node::GetLocalURI ( XMP_StringPtr * uriStr, XMP_StringLen * uriSize ) const
+{
+
+ if ( uriStr != 0 ) *uriStr = ""; // Set up empty defaults.
+ if ( uriSize != 0 ) *uriSize = 0;
+
+ if ( this->name.empty() ) return;
+
+ if ( XMP_NodeIsSchema ( this->options ) ) {
+
+ if ( uriStr != 0 ) *uriStr = this->name.c_str();
+ if ( uriSize != 0 ) *uriSize = this->name.size();
+
+ } else {
+
+ size_t colonPos = this->name.find_first_of(':');
+ if ( colonPos == XMP_VarString::npos ) return; // ! Name of array items is "[]".
+
+ XMP_VarString prefix ( this->name, 0, colonPos );
+ XMPMeta::GetNamespaceURI ( prefix.c_str(), uriStr, uriSize );
+
+ }
+
+}
+
+// =================================================================================================
+// Error notifications
+// ===================
+
+// -------------------------------------------------------------------------------------------------
+// SetDefaultErrorCallback
+// -----------------------
+
+/* class-static */ void
+XMPMeta::SetDefaultErrorCallback ( XMPMeta_ErrorCallbackWrapper wrapperProc,
+ XMPMeta_ErrorCallbackProc clientProc,
+ void * context,
+ XMP_Uns32 limit )
+{
+ XMP_Assert ( wrapperProc != 0 ); // Must always be set by the glue;
+
+ sDefaultErrorCallback.wrapperProc = wrapperProc;
+ sDefaultErrorCallback.clientProc = clientProc;
+ sDefaultErrorCallback.context = context;
+ sDefaultErrorCallback.limit = limit;
+
+} // SetDefaultErrorCallback
+
+// -------------------------------------------------------------------------------------------------
+// SetErrorCallback
+// ----------------
+
+void
+XMPMeta::SetErrorCallback ( XMPMeta_ErrorCallbackWrapper wrapperProc,
+ XMPMeta_ErrorCallbackProc clientProc,
+ void * context,
+ XMP_Uns32 limit )
+{
+ XMP_Assert ( wrapperProc != 0 ); // Must always be set by the glue;
+
+ this->errorCallback.Clear();
+ this->errorCallback.wrapperProc = wrapperProc;
+ this->errorCallback.clientProc = clientProc;
+ this->errorCallback.context = context;
+ this->errorCallback.limit = limit;
+
+} // SetErrorCallback
+
+// -------------------------------------------------------------------------------------------------
+// ResetErrorCallbackLimit
+// -----------------------
+
+void
+XMPMeta::ResetErrorCallbackLimit ( XMP_Uns32 limit )
+{
+
+ this->errorCallback.limit = limit;
+ this->errorCallback.notifications = 0;
+ this->errorCallback.topSeverity = kXMPErrSev_Recoverable;
+
+} // ResetErrorCallbackLimit
+
+// -------------------------------------------------------------------------------------------------
+// ErrorCallbackInfo::CanNotify
+// -------------------------------
+//
+// This is const just to be usable from const XMPMeta functions.
+
+bool XMPMeta::ErrorCallbackInfo::CanNotify() const
+{
+ XMP_Assert ( (this->clientProc == 0) || (this->wrapperProc != 0) );
+ return ( this->clientProc != 0);
+}
+
+// -------------------------------------------------------------------------------------------------
+// ErrorCallbackInfo::ClientCallbackWrapper
+// -------------------------------
+//
+// This is const just to be usable from const XMPMeta functions.
+
+bool XMPMeta::ErrorCallbackInfo::ClientCallbackWrapper ( XMP_StringPtr filePath, XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr messsage ) const
+{
+ XMP_Bool retValue = (*this->wrapperProc) ( this->clientProc, this->context, severity, cause, messsage );
+ return ConvertXMP_BoolToBool(retValue);
+}
+
+// =================================================================================================
diff --git a/gpr/source/lib/xmp_core/XMPMeta.hpp b/gpr/source/lib/xmp_core/XMPMeta.hpp
new file mode 100644
index 0000000..2542f79
--- /dev/null
+++ b/gpr/source/lib/xmp_core/XMPMeta.hpp
@@ -0,0 +1,428 @@
+#ifndef __XMPMeta_hpp__
+#define __XMPMeta_hpp__
+
+// =================================================================================================
+// Copyright 2003 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.
+// =================================================================================================
+
+#include "public/include/XMP_Environment.h"
+#include "public/include/XMP_Const.h"
+#include "XMPCore_Impl.hpp"
+#include "XMLParserAdapter.hpp"
+
+// -------------------------------------------------------------------------------------------------
+
+#ifndef DumpXMLParseTree
+ #define DumpXMLParseTree 0
+#endif
+
+extern XMP_VarString * xdefaultName; // Needed in XMPMeta-Parse.cpp, MoveExplicitAliases.
+
+class XMPIterator;
+class XMPUtils;
+
+// -------------------------------------------------------------------------------------------------
+
+class XMPMeta {
+public:
+
+ static void
+ GetVersionInfo ( XMP_VersionInfo * info );
+
+ static bool
+ Initialize();
+ static void
+ Terminate() RELEASE_NO_THROW;
+
+ // ---------------------------------------------------------------------------------------------
+
+ XMPMeta();
+
+ virtual ~XMPMeta() RELEASE_NO_THROW;
+
+ // ---------------------------------------------------------------------------------------------
+
+ static XMP_OptionBits
+ GetGlobalOptions();
+
+ static void
+ SetGlobalOptions ( XMP_OptionBits options );
+
+ // ---------------------------------------------------------------------------------------------
+
+ static XMP_Status
+ DumpNamespaces ( XMP_TextOutputProc outProc,
+ void * refCon );
+
+ // ---------------------------------------------------------------------------------------------
+
+ static bool
+ RegisterNamespace ( XMP_StringPtr namespaceURI,
+ XMP_StringPtr suggestedPrefix,
+ XMP_StringPtr * registeredPrefix,
+ XMP_StringLen * prefixSize );
+
+ static bool
+ GetNamespacePrefix ( XMP_StringPtr namespaceURI,
+ XMP_StringPtr * namespacePrefix,
+ XMP_StringLen * prefixSize );
+
+ static bool
+ GetNamespaceURI ( XMP_StringPtr namespacePrefix,
+ XMP_StringPtr * namespaceURI,
+ XMP_StringLen * uriSize );
+
+ static void
+ DeleteNamespace ( XMP_StringPtr namespaceURI );
+
+ // ---------------------------------------------------------------------------------------------
+
+ bool
+ GetProperty ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr * propValue,
+ XMP_StringLen * valueSize,
+ XMP_OptionBits * options ) const;
+
+ bool
+ GetArrayItem ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex,
+ XMP_StringPtr * itemValue,
+ XMP_StringLen * valueSize,
+ XMP_OptionBits * options ) const;
+
+ bool
+ GetStructField ( XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ XMP_StringPtr * fieldValue,
+ XMP_StringLen * valueSize,
+ XMP_OptionBits * options ) const;
+
+ bool
+ GetQualifier ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName,
+ XMP_StringPtr * qualValue,
+ XMP_StringLen * valueSize,
+ XMP_OptionBits * options ) const;
+
+ // ---------------------------------------------------------------------------------------------
+
+ void
+ SetProperty ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr propValue,
+ XMP_OptionBits options );
+
+ void
+ SetArrayItem ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex,
+ XMP_StringPtr itemValue,
+ XMP_OptionBits options );
+
+ void
+ AppendArrayItem ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_OptionBits arrayOptions,
+ XMP_StringPtr itemValue,
+ XMP_OptionBits options );
+
+ void
+ SetStructField ( XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ XMP_StringPtr fieldValue,
+ XMP_OptionBits options );
+
+ void
+ SetQualifier ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName,
+ XMP_StringPtr qualValue,
+ XMP_OptionBits options );
+
+ // ---------------------------------------------------------------------------------------------
+
+ void
+ DeleteProperty ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName );
+
+ void
+ DeleteArrayItem ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex );
+
+ void
+ DeleteStructField ( XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName );
+
+ void
+ DeleteQualifier ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName );
+
+ // ---------------------------------------------------------------------------------------------
+
+ bool
+ DoesPropertyExist ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName ) const;
+
+ bool
+ DoesArrayItemExist ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex ) const;
+
+ bool
+ DoesStructFieldExist ( XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName ) const;
+
+ bool
+ DoesQualifierExist ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName ) const;
+
+ // ---------------------------------------------------------------------------------------------
+
+ bool
+ GetLocalizedText ( XMP_StringPtr schemaNS,
+ XMP_StringPtr altTextName,
+ XMP_StringPtr genericLang,
+ XMP_StringPtr specificLang,
+ XMP_StringPtr * actualLang,
+ XMP_StringLen * langSize,
+ XMP_StringPtr * itemValue,
+ XMP_StringLen * valueSize,
+ XMP_OptionBits * options ) const;
+
+ void
+ SetLocalizedText ( XMP_StringPtr schemaNS,
+ XMP_StringPtr altTextName,
+ XMP_StringPtr genericLang,
+ XMP_StringPtr specificLang,
+ XMP_StringPtr itemValue,
+ XMP_OptionBits options );
+
+ void
+ DeleteLocalizedText ( XMP_StringPtr schemaNS,
+ XMP_StringPtr altTextName,
+ XMP_StringPtr genericLang,
+ XMP_StringPtr specificLang);
+
+ // ---------------------------------------------------------------------------------------------
+
+ bool
+ GetProperty_Bool ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ bool * propValue,
+ XMP_OptionBits * options ) const;
+
+ bool
+ GetProperty_Int ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_Int32 * propValue,
+ XMP_OptionBits * options ) const;
+
+ bool
+ GetProperty_Int64 ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_Int64 * propValue,
+ XMP_OptionBits * options ) const;
+
+ bool
+ GetProperty_Float ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ double * propValue,
+ XMP_OptionBits * options ) const;
+
+ bool
+ GetProperty_Date ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_DateTime * propValue,
+ XMP_OptionBits * options ) const;
+
+ // ---------------------------------------------------------------------------------------------
+
+ void
+ SetProperty_Bool ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ bool propValue,
+ XMP_OptionBits options );
+
+ void
+ SetProperty_Int ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_Int32 propValue,
+ XMP_OptionBits options );
+
+ void
+ SetProperty_Int64 ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_Int64 propValue,
+ XMP_OptionBits options );
+
+ void
+ SetProperty_Float ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ double propValue,
+ XMP_OptionBits options );
+
+ void
+ SetProperty_Date ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ const XMP_DateTime & propValue,
+ XMP_OptionBits options );
+
+ // ---------------------------------------------------------------------------------------------
+
+ void
+ GetObjectName ( XMP_StringPtr * namePtr,
+ XMP_StringLen * nameLen ) const;
+
+ void
+ SetObjectName ( XMP_StringPtr name );
+
+ XMP_OptionBits
+ GetObjectOptions() const;
+
+ void
+ SetObjectOptions ( XMP_OptionBits options );
+
+ void
+ Sort();
+
+ void
+ Erase();
+
+ void
+ Clone ( XMPMeta * clone, XMP_OptionBits options ) const;
+
+ XMP_Index
+ CountArrayItems ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName ) const;
+
+ void
+ DumpObject ( XMP_TextOutputProc outProc,
+ void * refCon ) const;
+
+ // ---------------------------------------------------------------------------------------------
+
+ void
+ ParseFromBuffer ( XMP_StringPtr buffer,
+ XMP_StringLen bufferSize,
+ XMP_OptionBits options );
+
+ void
+ SerializeToBuffer ( XMP_VarString * rdfString,
+ XMP_OptionBits options,
+ XMP_StringLen padding,
+ XMP_StringPtr newline,
+ XMP_StringPtr indent,
+ XMP_Index baseIndent ) const;
+
+ // ---------------------------------------------------------------------------------------------
+
+ static void
+ SetDefaultErrorCallback ( XMPMeta_ErrorCallbackWrapper wrapperProc,
+ XMPMeta_ErrorCallbackProc clientProc,
+ void * context,
+ XMP_Uns32 limit );
+
+ void
+ SetErrorCallback ( XMPMeta_ErrorCallbackWrapper wrapperProc,
+ XMPMeta_ErrorCallbackProc clientProc,
+ void * context,
+ XMP_Uns32 limit );
+
+ void
+ ResetErrorCallbackLimit ( XMP_Uns32 limit );
+
+ class ErrorCallbackInfo : public GenericErrorCallback {
+ public:
+
+ XMPMeta_ErrorCallbackWrapper wrapperProc;
+ XMPMeta_ErrorCallbackProc clientProc;
+ void * context;
+
+ ErrorCallbackInfo() : wrapperProc(0), clientProc(0), context(0) {};
+
+ void Clear() { this->wrapperProc = 0; this->clientProc = 0; this->context = 0;
+ GenericErrorCallback::Clear(); };
+
+ bool CanNotify() const;
+ bool ClientCallbackWrapper ( XMP_StringPtr filePath, XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr messsage ) const;
+ };
+
+ // =============================================================================================
+
+ // ---------------------------------------------------------------------------------------------
+ // - Everything is built out of standard nodes. Each node has a name, value, option flags, a
+ // vector of child nodes, and a vector of qualifier nodes.
+ //
+ // - The option flags are those passed to SetProperty and returned from GetProperty. They tell
+ // if the node is simple, a struct or an array; whether it has qualifiers, etc.
+ //
+ // - The name of the node is an XML qualified name, of the form "prefix:simple-name". Since we
+ // force all namespaces to be known and to have unique prefixes, this is semantically equivalent
+ // to using a URI and simple name pair.
+ //
+ // - Although the value part is only for leaf properties and the children part is only for
+ // structs and arrays, it is easier to simply have them in every node. This keeps things visible
+ // so that debugging is easier
+ //
+ // - The top level node children are the namespaces that contain properties, the next level are
+ // the top level properties, lower levels are the fields of structs or items of arrays. The name
+ // of the top level nodes is just the namespace prefix, with the colon terminator. The name of
+ // top level properties includes the namespace prefix.
+ //
+ // - Any property node, at any level, can have qualifiers. These are themselves general property
+ // nodes. And could in fact themselves have qualifiers!
+
+ // ! Expose the implementation so that file static functions can see the data.
+
+ XMP_Int32 clientRefs; // ! Must be signed to allow decrement from 0.
+ XMP_ReadWriteLock lock;
+
+ // ! Any data member changes must be propagted to the Clone function!
+
+ XMP_Node tree;
+ XMLParserAdapter * xmlParser;
+ ErrorCallbackInfo errorCallback;
+
+ friend class XMPIterator;
+ friend class XMPUtils;
+
+private:
+
+ // ! These are hidden on purpose:
+ XMPMeta ( const XMPMeta & /* original */ ) : tree(XMP_Node(0,"",0)), clientRefs(0), xmlParser(0)
+ { XMP_Throw ( "Call to hidden constructor", kXMPErr_InternalFailure ); };
+ void operator= ( const XMPMeta & /* rhs */ )
+ { XMP_Throw ( "Call to hidden operator=", kXMPErr_InternalFailure ); };
+
+ // Special support routines for parsing, here to be able to access the errorCallback.
+ void ProcessXMLTree ( XMP_OptionBits options );
+ bool ProcessXMLBuffer ( XMP_StringPtr buffer, XMP_StringLen xmpSize, bool lastClientCall );
+ void ProcessRDF ( const XML_Node & xmlTree, XMP_OptionBits options );
+
+}; // class XMPMeta
+
+// =================================================================================================
+
+#endif // __XMPMeta_hpp__
diff --git a/gpr/source/lib/xmp_core/XMPUtils-FileInfo.cpp b/gpr/source/lib/xmp_core/XMPUtils-FileInfo.cpp
new file mode 100644
index 0000000..19096dc
--- /dev/null
+++ b/gpr/source/lib/xmp_core/XMPUtils-FileInfo.cpp
@@ -0,0 +1,1493 @@
+// =================================================================================================
+// Copyright 2003 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.
+// =================================================================================================
+
+#include <algorithm> // For binary_search.
+
+#include "public/include/XMP_Environment.h" // ! This must be the first include!
+#include "XMPCore_Impl.hpp"
+
+#include "XMPUtils.hpp"
+
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <errno.h>
+
+#include <stdio.h> // For snprintf.
+
+#if XMP_WinBuild
+ #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning)
+#endif
+
+// =================================================================================================
+// Local Types and Constants
+// =========================
+
+typedef unsigned long UniCodePoint;
+
+enum UniCharKind {
+ UCK_normal,
+ UCK_space,
+ UCK_comma,
+ UCK_semicolon,
+ UCK_quote,
+ UCK_control
+};
+typedef enum UniCharKind UniCharKind;
+
+#define UnsByte(c) ((unsigned char)(c))
+#define UCP(u) ((UniCodePoint)(u))
+ // ! Needed on Windows (& PC Linux?) for inequalities with literals ito avoid sign extension.
+
+#ifndef TraceMultiFile
+ #define TraceMultiFile 0
+#endif
+
+// =================================================================================================
+// Static Variables
+// ================
+
+// =================================================================================================
+// Local Utilities
+// ===============
+
+// -------------------------------------------------------------------------------------------------
+// ClassifyCharacter
+// -----------------
+
+static void
+ClassifyCharacter ( XMP_StringPtr fullString, size_t offset,
+ UniCharKind * charKind, size_t * charSize, UniCodePoint * uniChar )
+{
+ *charKind = UCK_normal; // Assume typical case.
+
+ unsigned char currByte = UnsByte ( fullString[offset] );
+
+ if ( currByte < UnsByte(0x80) ) {
+
+ // ----------------------------------------
+ // We've got a single byte ASCII character.
+
+ *charSize = 1;
+ *uniChar = currByte;
+
+ if ( currByte > UnsByte(0x22) ) {
+
+ if ( currByte == UnsByte(0x2C) ) {
+ *charKind = UCK_comma;
+ } else if ( currByte == UnsByte(0x3B) ) {
+ *charKind = UCK_semicolon;
+ }
+ // [2674672] Discontinue to interpret square brackets
+ // as Asian quotes in XMPUtils::SeparateArrayItems(..))
+ // *** else if ( (currByte == UnsByte(0x5B)) || (currByte == UnsByte(0x5D)) ) {
+ // *** *charKind = UCK_quote; // ! ASCII '[' and ']' are used as quotes in Chinese and Korean.
+ // *** }
+
+ } else { // currByte <= 0x22
+
+ if ( currByte == UnsByte(0x22) ) {
+ *charKind = UCK_quote;
+ } else if ( currByte == UnsByte(0x21) ) {
+ *charKind = UCK_normal;
+ } else if ( currByte == UnsByte(0x20) ) {
+ *charKind = UCK_space;
+ } else {
+ *charKind = UCK_control;
+ }
+
+ }
+
+ } else { // currByte >= 0x80
+
+ // ---------------------------------------------------------------------------------------
+ // We've got a multibyte Unicode character. The first byte has the number of bytes and the
+ // highest order bits. The other bytes each add 6 more bits. Compose the UTF-32 form so we
+ // can classify directly with the Unicode code points. Order the upperBits tests to be
+ // fastest for Japan, probably the most common non-ASCII usage.
+
+ *charSize = 0;
+ *uniChar = currByte;
+ while ( (*uniChar & 0x80) != 0 ) { // Count the leading 1 bits in the byte.
+ ++(*charSize);
+ *uniChar = *uniChar << 1;
+ }
+ XMP_Assert ( (offset + *charSize) <= strlen(fullString) );
+
+ *uniChar = *uniChar & 0x7F; // Put the character bits in the bottom of uniChar.
+ *uniChar = *uniChar >> *charSize;
+
+ for ( size_t i = (offset + 1); i < (offset + *charSize); ++i ) {
+ *uniChar = (*uniChar << 6) | (UnsByte(fullString[i]) & 0x3F);
+ }
+
+ XMP_Uns32 upperBits = *uniChar >> 8; // First filter on just the high order 24 bits.
+
+ if ( upperBits == 0xFF ) { // U+FFxx
+
+ if ( *uniChar == UCP(0xFF0C) ) {
+ *charKind = UCK_comma; // U+FF0C, full width comma.
+ } else if ( *uniChar == UCP(0xFF1B) ) {
+ *charKind = UCK_semicolon; // U+FF1B, full width semicolon.
+ } else if ( *uniChar == UCP(0xFF64) ) {
+ *charKind = UCK_comma; // U+FF64, half width ideographic comma.
+ }
+
+ } else if ( upperBits == 0xFE ) { // U+FE--
+
+ if ( *uniChar == UCP(0xFE50) ) {
+ *charKind = UCK_comma; // U+FE50, small comma.
+ } else if ( *uniChar == UCP(0xFE51) ) {
+ *charKind = UCK_comma; // U+FE51, small ideographic comma.
+ } else if ( *uniChar == UCP(0xFE54) ) {
+ *charKind = UCK_semicolon; // U+FE54, small semicolon.
+ }
+
+ } else if ( upperBits == 0x30 ) { // U+30--
+
+ if ( *uniChar == UCP(0x3000) ) {
+ *charKind = UCK_space; // U+3000, ideographic space.
+ } else if ( *uniChar == UCP(0x3001) ) {
+ *charKind = UCK_comma; // U+3001, ideographic comma.
+ } else if ( (UCP(0x3008) <= *uniChar) && (*uniChar <= UCP(0x300F)) ) {
+ *charKind = UCK_quote; // U+3008..U+300F, various quotes.
+ } else if ( *uniChar == UCP(0x303F) ) {
+ *charKind = UCK_space; // U+303F, ideographic half fill space.
+ } else if ( (UCP(0x301D) <= *uniChar) && (*uniChar <= UCP(0x301F)) ) {
+ *charKind = UCK_quote; // U+301D..U+301F, double prime quotes.
+ }
+
+ } else if ( upperBits == 0x20 ) { // U+20--
+
+ if ( (UCP(0x2000) <= *uniChar) && (*uniChar <= UCP(0x200B)) ) {
+ *charKind = UCK_space; // U+2000..U+200B, en quad through zero width space.
+ } else if ( *uniChar == UCP(0x2015) ) {
+ *charKind = UCK_quote; // U+2015, dash quote.
+ } else if ( (UCP(0x2018) <= *uniChar) && (*uniChar <= UCP(0x201F)) ) {
+ *charKind = UCK_quote; // U+2018..U+201F, various quotes.
+ } else if ( *uniChar == UCP(0x2028) ) {
+ *charKind = UCK_control; // U+2028, line separator.
+ } else if ( *uniChar == UCP(0x2029) ) {
+ *charKind = UCK_control; // U+2029, paragraph separator.
+ } else if ( (*uniChar == UCP(0x2039)) || (*uniChar == UCP(0x203A)) ) {
+ *charKind = UCK_quote; // U+2039 and U+203A, guillemet quotes.
+ }
+
+ } else if ( upperBits == 0x06 ) { // U+06--
+
+ if ( *uniChar == UCP(0x060C) ) {
+ *charKind = UCK_comma; // U+060C, Arabic comma.
+ } else if ( *uniChar == UCP(0x061B) ) {
+ *charKind = UCK_semicolon; // U+061B, Arabic semicolon.
+ }
+
+ } else if ( upperBits == 0x05 ) { // U+05--
+
+ if ( *uniChar == UCP(0x055D) ) {
+ *charKind = UCK_comma; // U+055D, Armenian comma.
+ }
+
+ } else if ( upperBits == 0x03 ) { // U+03--
+
+ if ( *uniChar == UCP(0x037E) ) {
+ *charKind = UCK_semicolon; // U+037E, Greek "semicolon" (really a question mark).
+ }
+
+ } else if ( upperBits == 0x00 ) { // U+00--
+
+ if ( (*uniChar == UCP(0x00AB)) || (*uniChar == UCP(0x00BB)) ) {
+ *charKind = UCK_quote; // U+00AB and U+00BB, guillemet quotes.
+ }
+
+ }
+
+ }
+
+} // ClassifyCharacter
+
+
+// -------------------------------------------------------------------------------------------------
+// IsClosingingQuote
+// -----------------
+
+static inline bool
+IsClosingingQuote ( UniCodePoint uniChar, UniCodePoint openQuote, UniCodePoint closeQuote )
+{
+
+ if ( (uniChar == closeQuote) ||
+ ( (openQuote == UCP(0x301D)) && ((uniChar == UCP(0x301E)) || (uniChar == UCP(0x301F))) ) ) {
+ return true;
+ } else {
+ return false;
+ }
+
+} // IsClosingingQuote
+
+
+// -------------------------------------------------------------------------------------------------
+// IsSurroundingQuote
+// ------------------
+
+static inline bool
+IsSurroundingQuote ( UniCodePoint uniChar, UniCodePoint openQuote, UniCodePoint closeQuote )
+{
+
+ if ( (uniChar == openQuote) || IsClosingingQuote ( uniChar, openQuote, closeQuote ) ) {
+ return true;
+ } else {
+ return false;
+ }
+
+} // IsSurroundingQuote
+
+
+// -------------------------------------------------------------------------------------------------
+// GetClosingQuote
+// ---------------
+
+static UniCodePoint
+GetClosingQuote ( UniCodePoint openQuote )
+{
+ UniCodePoint closeQuote;
+
+ switch ( openQuote ) {
+
+ case UCP(0x0022) : closeQuote = UCP(0x0022); // ! U+0022 is both opening and closing.
+ break;
+ // *** [2674672] Discontinue to interpret square brackets
+ // *** as Asian quotes in XMPUtils::SeparateArrayItems(..))
+ // *** case UCP(0x005B) : closeQuote = UCP(0x005D);
+ // *** break;
+ case UCP(0x00AB) : closeQuote = UCP(0x00BB); // ! U+00AB and U+00BB are reversible.
+ break;
+ case UCP(0x00BB) : closeQuote = UCP(0x00AB);
+ break;
+ case UCP(0x2015) : closeQuote = UCP(0x2015); // ! U+2015 is both opening and closing.
+ break;
+ case UCP(0x2018) : closeQuote = UCP(0x2019);
+ break;
+ case UCP(0x201A) : closeQuote = UCP(0x201B);
+ break;
+ case UCP(0x201C) : closeQuote = UCP(0x201D);
+ break;
+ case UCP(0x201E) : closeQuote = UCP(0x201F);
+ break;
+ case UCP(0x2039) : closeQuote = UCP(0x203A); // ! U+2039 and U+203A are reversible.
+ break;
+ case UCP(0x203A) : closeQuote = UCP(0x2039);
+ break;
+ case UCP(0x3008) : closeQuote = UCP(0x3009);
+ break;
+ case UCP(0x300A) : closeQuote = UCP(0x300B);
+ break;
+ case UCP(0x300C) : closeQuote = UCP(0x300D);
+ break;
+ case UCP(0x300E) : closeQuote = UCP(0x300F);
+ break;
+ case UCP(0x301D) : closeQuote = UCP(0x301F); // ! U+301E also closes U+301D.
+ break;
+ default : closeQuote = 0;
+ break;
+
+ }
+
+ return closeQuote;
+
+} // GetClosingQuote
+
+
+// -------------------------------------------------------------------------------------------------
+// CodePointToUTF8
+// ---------------
+
+static void
+CodePointToUTF8 ( UniCodePoint uniChar, XMP_VarString & utf8Str )
+{
+ size_t i, byteCount;
+ XMP_Uns8 buffer [8];
+ UniCodePoint cpTemp;
+
+ if ( uniChar <= 0x7F ) {
+
+ i = 7;
+ byteCount = 1;
+ buffer[7] = char(uniChar);
+
+ } else {
+
+ // ---------------------------------------------------------------------------------------
+ // Copy the data bits from the low order end to the high order end, include the 0x80 mask.
+
+ i = 8;
+ cpTemp = uniChar;
+ while ( cpTemp != 0 ) {
+ -- i; // Exit with i pointing to the last byte stored.
+ buffer[i] = UnsByte(0x80) | (UnsByte(cpTemp) & 0x3F);
+ cpTemp = cpTemp >> 6;
+ }
+ byteCount = 8 - i; // The total number of bytes needed.
+ XMP_Assert ( (2 <= byteCount) && (byteCount <= 6) );
+
+ // -------------------------------------------------------------------------------------
+ // Make sure the high order byte can hold the byte count mask, compute and set the mask.
+
+ size_t bitCount = 0; // The number of data bits in the first byte.
+ for ( cpTemp = (buffer[i] & UnsByte(0x3F)); cpTemp != 0; cpTemp = cpTemp >> 1 ) bitCount += 1;
+ if ( bitCount > (8 - (byteCount + 1)) ) byteCount += 1;
+
+ i = 8 - byteCount; // First byte index and mask shift count.
+ XMP_Assert ( (0 <= i) && (i <= 6) );
+ buffer[i] |= (UnsByte(0xFF) << i) & UnsByte(0xFF); // AUDIT: Safe, i is between 0 and 6.
+
+ }
+
+ utf8Str.assign ( (char*)(&buffer[i]), byteCount );
+
+} // CodePointToUTF8
+
+
+// -------------------------------------------------------------------------------------------------
+// ApplyQuotes
+// -----------
+
+static void
+ApplyQuotes ( XMP_VarString * item, UniCodePoint openQuote, UniCodePoint closeQuote, bool allowCommas )
+{
+ bool prevSpace = false;
+ size_t charOffset, charLen;
+ UniCharKind charKind;
+ UniCodePoint uniChar;
+
+ // -----------------------------------------------------------------------------------------
+ // See if there are any separators in the value. Stop at the first occurrance. This is a bit
+ // tricky in order to make typical typing work conveniently. The purpose of applying quotes
+ // is to preserve the values when splitting them back apart. That is CatenateContainerItems
+ // and SeparateContainerItems must round trip properly. For the most part we only look for
+ // separators here. Internal quotes, as in -- Irving "Bud" Jones -- won't cause problems in
+ // the separation. An initial quote will though, it will make the value look quoted.
+
+ charOffset = 0;
+ ClassifyCharacter ( item->c_str(), charOffset, &charKind, &charLen, &uniChar );
+
+ if ( charKind != UCK_quote ) {
+
+ for ( charOffset = 0; size_t(charOffset) < item->size(); charOffset += charLen ) {
+
+ ClassifyCharacter ( item->c_str(), charOffset, &charKind, &charLen, &uniChar );
+
+ if ( charKind == UCK_space ) {
+ if ( prevSpace ) break; // Multiple spaces are a separator.
+ prevSpace = true;
+ } else {
+ prevSpace = false;
+ if ( (charKind == UCK_semicolon) || (charKind == UCK_control) ) break;
+ if ( (charKind == UCK_comma) && (! allowCommas) ) break;
+ }
+
+ }
+
+ }
+
+ if ( size_t(charOffset) < item->size() ) {
+
+ // --------------------------------------------------------------------------------------
+ // Create a quoted copy, doubling any internal quotes that match the outer ones. Internal
+ // quotes did not stop the "needs quoting" search, but they do need doubling. So we have
+ // to rescan the front of the string for quotes. Handle the special case of U+301D being
+ // closed by either U+301E or U+301F.
+
+ XMP_VarString newItem;
+ size_t splitPoint;
+
+ for ( splitPoint = 0; splitPoint <= charOffset; ++splitPoint ) {
+ ClassifyCharacter ( item->c_str(), splitPoint, &charKind, &charLen, &uniChar );
+ if ( charKind == UCK_quote ) break;
+ }
+
+ CodePointToUTF8 ( openQuote, newItem );
+ newItem.append ( *item, 0, splitPoint ); // Copy the leading "normal" portion.
+
+ for ( charOffset = splitPoint; size_t(charOffset) < item->size(); charOffset += charLen ) {
+ ClassifyCharacter ( item->c_str(), charOffset, &charKind, &charLen, &uniChar );
+ newItem.append ( *item, charOffset, charLen );
+ if ( (charKind == UCK_quote) && IsSurroundingQuote ( uniChar, openQuote, closeQuote ) ) {
+ newItem.append ( *item, charOffset, charLen );
+ }
+ }
+
+ XMP_VarString closeStr;
+ CodePointToUTF8 ( closeQuote, closeStr );
+ newItem.append ( closeStr );
+
+ *item = newItem;
+
+ }
+
+} // ApplyQuotes
+
+
+// -------------------------------------------------------------------------------------------------
+// IsInternalProperty
+// ------------------
+
+// *** Need static checks of the schema prefixes!
+
+static const char * kExternalxmpDM[] =
+ { "xmpDM:album",
+ "xmpDM:altTapeName",
+ "xmpDM:altTimecode",
+ "xmpDM:artist",
+ "xmpDM:cameraAngle",
+ "xmpDM:cameraLabel",
+ "xmpDM:cameraModel",
+ "xmpDM:cameraMove",
+ "xmpDM:client",
+ "xmpDM:comment",
+ "xmpDM:composer",
+ "xmpDM:director",
+ "xmpDM:directorPhotography",
+ "xmpDM:engineer",
+ "xmpDM:genre",
+ "xmpDM:good",
+ "xmpDM:instrument",
+ "xmpDM:logComment",
+ "xmpDM:projectName",
+ "xmpDM:releaseDate",
+ "xmpDM:scene",
+ "xmpDM:shotDate",
+ "xmpDM:shotDay",
+ "xmpDM:shotLocation",
+ "xmpDM:shotName",
+ "xmpDM:shotNumber",
+ "xmpDM:shotSize",
+ "xmpDM:speakerPlacement",
+ "xmpDM:takeNumber",
+ "xmpDM:tapeName",
+ "xmpDM:trackNumber",
+ "xmpDM:videoAlphaMode",
+ "xmpDM:videoAlphaPremultipleColor",
+ 0 }; // ! Must have zero sentinel!
+
+typedef const char ** CharStarIterator; // Used for binary search of kExternalxmpDM;
+static const char ** kLastExternalxmpDM = 0; // Set on first use.
+static int CharStarLess (const char * left, const char * right )
+ { return (strcmp ( left, right ) < 0); }
+
+#define IsExternalProperty(s,p) (! IsInternalProperty ( s, p ))
+
+static bool
+IsInternalProperty ( const XMP_VarString & schema, const XMP_VarString & prop )
+{
+ bool isInternal = false;
+
+ if ( schema == kXMP_NS_DC ) {
+
+ if ( (prop == "dc:format") ||
+ (prop == "dc:language") ) {
+ isInternal = true;
+ }
+
+ } else if ( schema == kXMP_NS_XMP ) {
+
+ if ( (prop == "xmp:BaseURL") ||
+ (prop == "xmp:CreatorTool") ||
+ (prop == "xmp:Format") ||
+ (prop == "xmp:Locale") ||
+ (prop == "xmp:MetadataDate") ||
+ (prop == "xmp:ModifyDate") ) {
+ isInternal = true;
+ }
+
+ } else if ( schema == kXMP_NS_PDF ) {
+
+ if ( (prop == "pdf:BaseURL") ||
+ (prop == "pdf:Creator") ||
+ (prop == "pdf:ModDate") ||
+ (prop == "pdf:PDFVersion") ||
+ (prop == "pdf:Producer") ) {
+ isInternal = true;
+ }
+
+ } else if ( schema == kXMP_NS_TIFF ) {
+
+ isInternal = true; // ! The TIFF properties are internal by default.
+ if ( (prop == "tiff:ImageDescription") || // ! ImageDescription, Artist, and Copyright are aliased.
+ (prop == "tiff:Artist") ||
+ (prop == "tiff:Copyright") ) {
+ isInternal = false;
+ }
+
+ } else if ( schema == kXMP_NS_EXIF ) {
+
+ isInternal = true; // ! The EXIF properties are internal by default.
+ if ( prop == "exif:UserComment" ) isInternal = false;
+
+ } else if ( schema == kXMP_NS_EXIF_Aux ) {
+
+ isInternal = true; // ! The EXIF Aux properties are internal by default.
+
+ } else if ( schema == kXMP_NS_Photoshop ) {
+
+ if ( (prop == "photoshop:ICCProfile") ||
+ (prop == "photoshop:TextLayers") ) isInternal = true;
+
+ } else if ( schema == kXMP_NS_CameraRaw ) {
+
+ isInternal = true; // All of crs: is internal, they are processing settings.
+
+ } else if ( schema == kXMP_NS_DM ) {
+
+ // ! Most of the xmpDM schema is internal, and unknown properties default to internal.
+ if ( kLastExternalxmpDM == 0 ) {
+ for ( kLastExternalxmpDM = &kExternalxmpDM[0]; *kLastExternalxmpDM != 0; ++kLastExternalxmpDM ) {}
+ }
+ isInternal = (! std::binary_search ( &kExternalxmpDM[0], kLastExternalxmpDM, prop.c_str(), CharStarLess ));
+
+ } else if ( schema == kXMP_NS_Script ) {
+
+ isInternal = true; // ! Most of the xmpScript schema is internal, and unknown properties default to internal.
+ if ( (prop == "xmpScript:action") || (prop == "xmpScript:character") || (prop == "xmpScript:dialog") ||
+ (prop == "xmpScript:sceneSetting") || (prop == "xmpScript:sceneTimeOfDay") ) {
+ isInternal = false;
+ }
+
+ } else if ( schema == kXMP_NS_BWF ) {
+
+ if ( prop == "bext:version" ) isInternal = true;
+
+ } else if ( schema == kXMP_NS_AdobeStockPhoto ) {
+
+ isInternal = true; // ! The bmsp schema has only internal properties.
+
+ } else if ( schema == kXMP_NS_XMP_MM ) {
+
+ isInternal = true; // ! The xmpMM schema has only internal properties.
+
+ } else if ( schema == kXMP_NS_XMP_Text ) {
+
+ isInternal = true; // ! The xmpT schema has only internal properties.
+
+ } else if ( schema == kXMP_NS_XMP_PagedFile ) {
+
+ isInternal = true; // ! The xmpTPg schema has only internal properties.
+
+ } else if ( schema == kXMP_NS_XMP_Graphics ) {
+
+ isInternal = true; // ! The xmpG schema has only internal properties.
+
+ } else if ( schema == kXMP_NS_XMP_Image ) {
+
+ isInternal = true; // ! The xmpGImg schema has only internal properties.
+
+ } else if ( schema == kXMP_NS_XMP_Font ) {
+
+ isInternal = true; // ! The stFNT schema has only internal properties.
+
+ }
+
+ return isInternal;
+
+} // IsInternalProperty
+
+
+// -------------------------------------------------------------------------------------------------
+// RemoveSchemaChildren
+// --------------------
+
+static void
+RemoveSchemaChildren ( XMP_NodePtrPos schemaPos, bool doAll )
+{
+ XMP_Node * schemaNode = *schemaPos;
+ XMP_Assert ( XMP_NodeIsSchema ( schemaNode->options ) );
+
+ // ! Iterate backwards to reduce shuffling as children are erased and to simplify the logic for
+ // ! denoting the current child. (Erasing child n makes the old n+1 now be n.)
+
+ size_t propCount = schemaNode->children.size();
+ XMP_NodePtrPos beginPos = schemaNode->children.begin();
+
+ for ( size_t propNum = propCount-1, propLim = (size_t)(-1); propNum != propLim; --propNum ) {
+ XMP_NodePtrPos currProp = beginPos + propNum;
+ if ( doAll || IsExternalProperty ( schemaNode->name, (*currProp)->name ) ) {
+ delete *currProp; // ! Both delete the node and erase the pointer from the parent.
+ schemaNode->children.erase ( currProp );
+ }
+ }
+
+ if ( schemaNode->children.empty() ) {
+ XMP_Node * tree = schemaNode->parent;
+ tree->children.erase ( schemaPos );
+ delete schemaNode;
+ }
+
+} // RemoveSchemaChildren
+
+
+// -------------------------------------------------------------------------------------------------
+// ItemValuesMatch
+// ---------------
+//
+// Does the value comparisons for array merging as part of XMPUtils::AppendProperties.
+
+static bool
+ItemValuesMatch ( const XMP_Node * leftNode, const XMP_Node * rightNode )
+{
+ const XMP_OptionBits leftForm = leftNode->options & kXMP_PropCompositeMask;
+ const XMP_OptionBits rightForm = leftNode->options & kXMP_PropCompositeMask;
+
+ if ( leftForm != rightForm ) return false;
+
+ if ( leftForm == 0 ) {
+
+ // Simple nodes, check the values and xml:lang qualifiers.
+
+ if ( leftNode->value != rightNode->value ) return false;
+ if ( (leftNode->options & kXMP_PropHasLang) != (rightNode->options & kXMP_PropHasLang) ) return false;
+ if ( leftNode->options & kXMP_PropHasLang ) {
+ if ( leftNode->qualifiers[0]->value != rightNode->qualifiers[0]->value ) return false;
+ }
+
+ } else if ( leftForm == kXMP_PropValueIsStruct ) {
+
+ // Struct nodes, see if all fields match, ignoring order.
+
+ if ( leftNode->children.size() != rightNode->children.size() ) return false;
+
+ for ( size_t leftNum = 0, leftLim = leftNode->children.size(); leftNum != leftLim; ++leftNum ) {
+ const XMP_Node * leftField = leftNode->children[leftNum];
+ const XMP_Node * rightField = FindConstChild ( rightNode, leftField->name.c_str() );
+ if ( (rightField == 0) || (! ItemValuesMatch ( leftField, rightField )) ) return false;
+ }
+
+ } else {
+
+ // Array nodes, see if the "leftNode" values are present in the "rightNode", ignoring order, duplicates,
+ // and extra values in the rightNode-> The rightNode is the destination for AppendProperties.
+
+ XMP_Assert ( leftForm & kXMP_PropValueIsArray );
+
+ for ( size_t leftNum = 0, leftLim = leftNode->children.size(); leftNum != leftLim; ++leftNum ) {
+
+ const XMP_Node * leftItem = leftNode->children[leftNum];
+
+ size_t rightNum, rightLim;
+ for ( rightNum = 0, rightLim = rightNode->children.size(); rightNum != rightLim; ++rightNum ) {
+ const XMP_Node * rightItem = rightNode->children[rightNum];
+ if ( ItemValuesMatch ( leftItem, rightItem ) ) break;
+ }
+ if ( rightNum == rightLim ) return false;
+
+ }
+
+ }
+
+ return true; // All of the checks passed.
+
+} // ItemValuesMatch
+
+
+// -------------------------------------------------------------------------------------------------
+// AppendSubtree
+// -------------
+//
+// The main implementation of XMPUtils::AppendProperties. See the description in TXMPMeta.hpp.
+
+static void
+AppendSubtree ( const XMP_Node * sourceNode, XMP_Node * destParent,
+ const bool mergeCompound, const bool replaceOld, const bool deleteEmpty )
+{
+ XMP_NodePtrPos destPos;
+ XMP_Node * destNode = FindChildNode ( destParent, sourceNode->name.c_str(), kXMP_ExistingOnly, &destPos );
+
+ bool valueIsEmpty = false;
+ if ( XMP_PropIsSimple ( sourceNode->options ) ) {
+ valueIsEmpty = sourceNode->value.empty();
+ } else {
+ valueIsEmpty = sourceNode->children.empty();
+ }
+
+ if ( valueIsEmpty ) {
+ if ( deleteEmpty && (destNode != 0) ) {
+ delete ( destNode );
+ destParent->children.erase ( destPos );
+ }
+ return; // ! Done, empty values are either ignored or cause deletions.
+ }
+
+ if ( destNode == 0 ) {
+ // The one easy case, the destination does not exist.
+ destNode = CloneSubtree ( sourceNode, destParent, true /* skipEmpty */ );
+ XMP_Assert ( (destNode == 0) || (! destNode->value.empty()) || (! destNode->children.empty()) );
+ return;
+ }
+
+ // If we get here we're going to modify an existing property, either replacing or merging.
+
+ XMP_Assert ( (! valueIsEmpty) && (destNode != 0) );
+
+ XMP_OptionBits sourceForm = sourceNode->options & kXMP_PropCompositeMask;
+ XMP_OptionBits destForm = destNode->options & kXMP_PropCompositeMask;
+
+ bool replaceThis = replaceOld; // ! Don't modify replaceOld, it gets passed to inner calls.
+ if ( mergeCompound && (! XMP_PropIsSimple ( sourceForm )) ) replaceThis = false;
+
+ if ( replaceThis ) {
+
+ destNode->value = sourceNode->value; // *** Should use SetNode.
+ destNode->options = sourceNode->options;
+ destNode->RemoveChildren();
+ destNode->RemoveQualifiers();
+ CloneOffspring ( sourceNode, destNode, true /* skipEmpty */ );
+
+ if ( (! XMP_PropIsSimple ( destNode->options )) && destNode->children.empty() ) {
+ // Don't keep an empty array or struct. The source might be implicitly empty due to
+ // all children being empty. In this case CloneOffspring should skip them.
+ DeleteSubtree ( destPos );
+ }
+
+ return;
+
+ }
+
+ // From here on are cases for merging arrays or structs.
+
+ if ( XMP_PropIsSimple ( sourceForm ) || (sourceForm != destForm) ) return;
+
+ if ( sourceForm == kXMP_PropValueIsStruct ) {
+
+ // To merge a struct process the fields recursively. E.g. add simple missing fields. The
+ // recursive call to AppendSubtree will handle deletion for fields with empty values.
+
+ for ( size_t sourceNum = 0, sourceLim = sourceNode->children.size(); sourceNum != sourceLim && destNode!= NULL; ++sourceNum ) {
+ const XMP_Node * sourceField = sourceNode->children[sourceNum];
+ AppendSubtree ( sourceField, destNode, mergeCompound, replaceOld, deleteEmpty );
+ if ( deleteEmpty && destNode->children.empty() ) {
+ delete ( destNode );
+ destParent->children.erase ( destPos );
+ }
+ }
+
+ } else if ( sourceForm & kXMP_PropArrayIsAltText ) {
+
+ // Merge AltText arrays by the xml:lang qualifiers. Make sure x-default is first. Make a
+ // special check for deletion of empty values. Meaningful in AltText arrays because the
+ // xml:lang qualifier provides unambiguous source/dest correspondence.
+
+ XMP_Assert ( mergeCompound );
+
+ for ( size_t sourceNum = 0, sourceLim = sourceNode->children.size(); sourceNum != sourceLim && destNode!= NULL; ++sourceNum ) {
+
+ const XMP_Node * sourceItem = sourceNode->children[sourceNum];
+ if ( sourceItem->qualifiers.empty() || (sourceItem->qualifiers[0]->name != "xml:lang") ) continue;
+
+ XMP_Index destIndex = LookupLangItem ( destNode, sourceItem->qualifiers[0]->value );
+
+ if ( sourceItem->value.empty() ) {
+
+ if ( deleteEmpty && (destIndex != -1) ) {
+ delete ( destNode->children[destIndex] );
+ destNode->children.erase ( destNode->children.begin() + destIndex );
+ if ( destNode->children.empty() ) {
+ delete ( destNode );
+ destParent->children.erase ( destPos );
+ }
+ }
+
+ } else {
+
+ if ( destIndex != -1 ) {
+
+ // The source and dest arrays both have this language item.
+
+ if ( replaceOld ) { // ! Yes, check replaceOld not replaceThis!
+ destNode->children[destIndex]->value = sourceItem->value;
+ }
+
+ } else {
+
+ // The dest array does not have this language item, add it.
+
+ if ( (sourceItem->qualifiers[0]->value != "x-default") || destNode->children.empty() ) {
+ // Typical case, empty dest array or not "x-default". Non-empty should always have "x-default".
+ CloneSubtree ( sourceItem, destNode, true /* skipEmpty */ );
+ } else {
+ // Edge case, non-empty dest array had no "x-default", insert that at the beginning.
+ XMP_Node * destItem = new XMP_Node ( destNode, sourceItem->name, sourceItem->value, sourceItem->options );
+ CloneOffspring ( sourceItem, destItem, true /* skipEmpty */ );
+ destNode->children.insert ( destNode->children.begin(), destItem );
+ }
+
+ }
+
+ }
+
+ }
+
+ } else if ( sourceForm & kXMP_PropValueIsArray ) {
+
+ // Merge other arrays by item values. Don't worry about order or duplicates. Source
+ // items with empty values do not cause deletion, that conflicts horribly with merging.
+
+ for ( size_t sourceNum = 0, sourceLim = sourceNode->children.size(); sourceNum != sourceLim; ++sourceNum ) {
+ const XMP_Node * sourceItem = sourceNode->children[sourceNum];
+
+ size_t destNum, destLim;
+ for ( destNum = 0, destLim = destNode->children.size(); destNum != destLim; ++destNum ) {
+ const XMP_Node * destItem = destNode->children[destNum];
+ if ( ItemValuesMatch ( sourceItem, destItem ) ) break;
+ }
+ if ( destNum == destLim ) CloneSubtree ( sourceItem, destNode, true /* skipEmpty */ );
+
+ }
+
+ }
+
+} // AppendSubtree
+
+
+// =================================================================================================
+// Class Static Functions
+// ======================
+
+// -------------------------------------------------------------------------------------------------
+// CatenateArrayItems
+// ------------------
+
+/* class static */ void
+XMPUtils::CatenateArrayItems ( const XMPMeta & xmpObj,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr separator,
+ XMP_StringPtr quotes,
+ XMP_OptionBits options,
+ XMP_VarString * catedStr )
+{
+ XMP_Assert ( (schemaNS != 0) && (arrayName != 0) ); // ! Enforced by wrapper.
+ XMP_Assert ( (separator != 0) && (quotes != 0) && (catedStr != 0) ); // ! Enforced by wrapper.
+
+ size_t strLen, strPos, charLen;
+ UniCharKind charKind;
+ UniCodePoint currUCP, openQuote, closeQuote;
+
+ const bool allowCommas = ((options & kXMPUtil_AllowCommas) != 0);
+
+ const XMP_Node * arrayNode = 0; // ! Move up to avoid gcc complaints.
+ XMP_OptionBits arrayForm = 0;
+ const XMP_Node * currItem = 0;
+
+ // Make sure the separator is OK. It must be one semicolon surrounded by zero or more spaces.
+ // Any of the recognized semicolons or spaces are allowed.
+
+ strPos = 0;
+ strLen = strlen ( separator );
+ bool haveSemicolon = false;
+
+ while ( strPos < strLen ) {
+ ClassifyCharacter ( separator, strPos, &charKind, &charLen, &currUCP );
+ strPos += charLen;
+ if ( charKind == UCK_semicolon ) {
+ if ( haveSemicolon ) XMP_Throw ( "Separator can have only one semicolon", kXMPErr_BadParam );
+ haveSemicolon = true;
+ } else if ( charKind != UCK_space ) {
+ XMP_Throw ( "Separator can have only spaces and one semicolon", kXMPErr_BadParam );
+ }
+ };
+ if ( ! haveSemicolon ) XMP_Throw ( "Separator must have one semicolon", kXMPErr_BadParam );
+
+ // Make sure the open and close quotes are a legitimate pair.
+
+ strLen = strlen ( quotes );
+ ClassifyCharacter ( quotes, 0, &charKind, &charLen, &openQuote );
+ if ( charKind != UCK_quote ) XMP_Throw ( "Invalid quoting character", kXMPErr_BadParam );
+
+ if ( charLen == strLen ) {
+ closeQuote = openQuote;
+ } else {
+ strPos = charLen;
+ ClassifyCharacter ( quotes, strPos, &charKind, &charLen, &closeQuote );
+ if ( charKind != UCK_quote ) XMP_Throw ( "Invalid quoting character", kXMPErr_BadParam );
+ if ( (strPos + charLen) != strLen ) XMP_Throw ( "Quoting string too long", kXMPErr_BadParam );
+ }
+ if ( closeQuote != GetClosingQuote ( openQuote ) ) XMP_Throw ( "Mismatched quote pair", kXMPErr_BadParam );
+
+ // Return an empty result if the array does not exist, hurl if it isn't the right form.
+
+ catedStr->erase();
+
+ XMP_ExpandedXPath arrayPath;
+ ExpandXPath ( schemaNS, arrayName, &arrayPath );
+
+ arrayNode = FindConstNode ( &xmpObj.tree, arrayPath );
+ if ( arrayNode == 0 ) return;
+
+ arrayForm = arrayNode->options & kXMP_PropCompositeMask;
+ if ( (! (arrayForm & kXMP_PropValueIsArray)) || (arrayForm & kXMP_PropArrayIsAlternate) ) {
+ XMP_Throw ( "Named property must be non-alternate array", kXMPErr_BadParam );
+ }
+ if ( arrayNode->children.empty() ) return;
+
+ // Build the result, quoting the array items, adding separators. Hurl if any item isn't simple.
+ // Start the result with the first value, then add the rest with a preceeding separator.
+
+ currItem = arrayNode->children[0];
+
+ if ( (currItem->options & kXMP_PropCompositeMask) != 0 ) XMP_Throw ( "Array items must be simple", kXMPErr_BadParam );
+ *catedStr = currItem->value;
+ ApplyQuotes ( catedStr, openQuote, closeQuote, allowCommas );
+
+ for ( size_t itemNum = 1, itemLim = arrayNode->children.size(); itemNum != itemLim; ++itemNum ) {
+ const XMP_Node * item = arrayNode->children[itemNum];
+ if ( (item->options & kXMP_PropCompositeMask) != 0 ) XMP_Throw ( "Array items must be simple", kXMPErr_BadParam );
+ XMP_VarString tempStr ( item->value );
+ ApplyQuotes ( &tempStr, openQuote, closeQuote, allowCommas );
+ *catedStr += separator;
+ *catedStr += tempStr;
+ }
+
+} // CatenateArrayItems
+
+
+// -------------------------------------------------------------------------------------------------
+// SeparateArrayItems
+// ------------------
+
+/* class static */ void
+XMPUtils::SeparateArrayItems ( XMPMeta * xmpObj,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_OptionBits options,
+ XMP_StringPtr catedStr )
+{
+ XMP_Assert ( (schemaNS != 0) && (arrayName != 0) && (catedStr != 0) ); // ! Enforced by wrapper.
+
+ XMP_VarString itemValue;
+ size_t itemStart, itemEnd;
+ size_t nextSize, charSize = 0; // Avoid VS uninit var warnings.
+ UniCharKind nextKind, charKind = UCK_normal;
+ UniCodePoint nextChar, uniChar = 0;
+
+ // Extract "special" option bits, verify and normalize the others.
+
+ bool preserveCommas = false;
+ if ( options & kXMPUtil_AllowCommas ) {
+ preserveCommas = true;
+ options ^= kXMPUtil_AllowCommas;
+ }
+
+ options = VerifySetOptions ( options, 0 ); // Keep a zero value, has special meaning below.
+ if ( options & ~kXMP_PropArrayFormMask ) XMP_Throw ( "Options can only provide array form", kXMPErr_BadOptions );
+
+ // Find the array node, make sure it is OK. Move the current children aside, to be readded later if kept.
+
+ XMP_ExpandedXPath arrayPath;
+ ExpandXPath ( schemaNS, arrayName, &arrayPath );
+ XMP_Node * arrayNode = FindNode ( &xmpObj->tree, arrayPath, kXMP_ExistingOnly );
+
+ if ( arrayNode != 0 ) {
+ // The array exists, make sure the form is compatible. Zero arrayForm means take what exists.
+ XMP_OptionBits arrayForm = arrayNode->options & kXMP_PropArrayFormMask;
+ if ( (arrayForm == 0) || (arrayForm & kXMP_PropArrayIsAlternate) ) {
+ XMP_Throw ( "Named property must be non-alternate array", kXMPErr_BadXPath );
+ }
+ if ( (options != 0) && (options != arrayForm) ) XMP_Throw ( "Mismatch of specified and existing array form", kXMPErr_BadXPath ); // *** Right error?
+ } else {
+ // The array does not exist, try to create it.
+ arrayNode = FindNode ( &xmpObj->tree, arrayPath, kXMP_CreateNodes, (options | kXMP_PropValueIsArray) );
+ if ( arrayNode == 0 ) XMP_Throw ( "Failed to create named array", kXMPErr_BadXPath );
+ }
+
+ XMP_NodeOffspring oldChildren ( arrayNode->children );
+ size_t oldChildCount = oldChildren.size();
+ arrayNode->children.clear();
+
+ // Extract the item values one at a time, until the whole input string is done. Be very careful
+ // in the extraction about the string positions. They are essentially byte pointers, while the
+ // contents are UTF-8. Adding or subtracting 1 does not necessarily move 1 Unicode character!
+
+ size_t endPos = strlen ( catedStr );
+
+ itemEnd = 0;
+ while ( itemEnd < endPos ) {
+
+ // Skip any leading spaces and separation characters. Always skip commas here. They can be
+ // kept when within a value, but not when alone between values.
+
+ for ( itemStart = itemEnd; itemStart < endPos; itemStart += charSize ) {
+ ClassifyCharacter ( catedStr, itemStart, &charKind, &charSize, &uniChar );
+ if ( (charKind == UCK_normal) || (charKind == UCK_quote) ) break;
+ }
+ if ( itemStart >= endPos ) break;
+
+ if ( charKind != UCK_quote ) {
+
+ // This is not a quoted value. Scan for the end, create an array item from the substring.
+
+ for ( itemEnd = itemStart; itemEnd < endPos; itemEnd += charSize ) {
+
+ ClassifyCharacter ( catedStr, itemEnd, &charKind, &charSize, &uniChar );
+
+ if ( (charKind == UCK_normal) || (charKind == UCK_quote) ) continue;
+ if ( (charKind == UCK_comma) && preserveCommas ) continue;
+ if ( charKind != UCK_space ) break;
+
+ if ( (itemEnd + charSize) >= endPos ) break; // Anything left?
+ ClassifyCharacter ( catedStr, (itemEnd+charSize), &nextKind, &nextSize, &nextChar );
+ if ( (nextKind == UCK_normal) || (nextKind == UCK_quote) ) continue;
+ if ( (nextKind == UCK_comma) && preserveCommas ) continue;
+ break; // Have multiple spaces, or a space followed by a separator.
+
+ }
+
+ itemValue.assign ( catedStr, itemStart, (itemEnd - itemStart) );
+
+ } else {
+
+ // Accumulate quoted values into a local string, undoubling internal quotes that
+ // match the surrounding quotes. Do not undouble "unmatching" quotes.
+
+ UniCodePoint openQuote = uniChar;
+ UniCodePoint closeQuote = GetClosingQuote ( openQuote );
+
+ itemStart += charSize; // Skip the opening quote;
+ itemValue.erase();
+
+ for ( itemEnd = itemStart; itemEnd < endPos; itemEnd += charSize ) {
+
+ ClassifyCharacter ( catedStr, itemEnd, &charKind, &charSize, &uniChar );
+
+ if ( (charKind != UCK_quote) || (! IsSurroundingQuote ( uniChar, openQuote, closeQuote)) ) {
+
+ // This is not a matching quote, just append it to the item value.
+ itemValue.append ( catedStr, itemEnd, charSize );
+
+ } else {
+
+ // This is a "matching" quote. Is it doubled, or the final closing quote? Tolerate
+ // various edge cases like undoubled opening (non-closing) quotes, or end of input.
+
+ if ( (itemEnd + charSize) < endPos ) {
+ ClassifyCharacter ( catedStr, itemEnd+charSize, &nextKind, &nextSize, &nextChar );
+ } else {
+ nextKind = UCK_semicolon; nextSize = 0; nextChar = 0x3B;
+ }
+
+ if ( uniChar == nextChar ) {
+ // This is doubled, copy it and skip the double.
+ itemValue.append ( catedStr, itemEnd, charSize );
+ itemEnd += nextSize; // Loop will add in charSize.
+ } else if ( ! IsClosingingQuote ( uniChar, openQuote, closeQuote ) ) {
+ // This is an undoubled, non-closing quote, copy it.
+ itemValue.append ( catedStr, itemEnd, charSize );
+ } else {
+ // This is an undoubled closing quote, skip it and exit the loop.
+ itemEnd += charSize;
+ break;
+ }
+
+ }
+
+ } // Loop to accumulate the quoted value.
+
+ }
+
+ // Add the separated item to the array. Keep a matching old value in case it had separators.
+
+ size_t oldChild;
+ for ( oldChild = 0; oldChild < oldChildCount; ++oldChild ) {
+ if ( (oldChildren[oldChild] != 0) && (itemValue == oldChildren[oldChild]->value) ) break;
+ }
+
+ XMP_Node * newItem = 0;
+ if ( oldChild == oldChildCount ) {
+ newItem = new XMP_Node ( arrayNode, kXMP_ArrayItemName, itemValue.c_str(), 0 );
+ } else {
+ newItem = oldChildren[oldChild];
+ oldChildren[oldChild] = 0; // ! Don't match again, let duplicates be seen.
+ }
+ arrayNode->children.push_back ( newItem );
+
+ } // Loop through all of the returned items.
+
+ // Delete any of the old children that were not kept.
+ for ( size_t i = 0; i < oldChildCount; ++i ) {
+ if ( oldChildren[i] != 0 ) delete oldChildren[i];
+ }
+
+} // SeparateArrayItems
+
+
+// -------------------------------------------------------------------------------------------------
+// ApplyTemplate
+// -------------
+
+/* class static */ void
+XMPUtils::ApplyTemplate ( XMPMeta * workingXMP,
+ const XMPMeta & templateXMP,
+ XMP_OptionBits actions )
+{
+ bool doClear = XMP_OptionIsSet ( actions, kXMPTemplate_ClearUnnamedProperties );
+ bool doAdd = XMP_OptionIsSet ( actions, kXMPTemplate_AddNewProperties );
+ bool doReplace = XMP_OptionIsSet ( actions, kXMPTemplate_ReplaceExistingProperties );
+
+ bool deleteEmpty = XMP_OptionIsSet ( actions, kXMPTemplate_ReplaceWithDeleteEmpty );
+ doReplace |= deleteEmpty; // Delete-empty implies Replace.
+ deleteEmpty &= (! doClear); // Clear implies not delete-empty, but keep the implicit Replace.
+
+ bool doAll = XMP_OptionIsSet ( actions, kXMPTemplate_IncludeInternalProperties );
+
+ // ! In several places we do loops backwards so that deletions do not perturb the remaining indices.
+ // ! These loops use ordinals (size .. 1), we must use a zero based index inside the loop.
+
+ if ( doClear ) {
+
+ // Visit the top level working properties, delete if not in the template.
+
+ for ( size_t schemaOrdinal = workingXMP->tree.children.size(); schemaOrdinal > 0; --schemaOrdinal ) {
+
+ size_t schemaNum = schemaOrdinal-1; // ! Convert ordinal to index!
+ XMP_Node * workingSchema = workingXMP->tree.children[schemaNum];
+ const XMP_Node * templateSchema = FindConstSchema ( &templateXMP.tree, workingSchema->name.c_str() );
+
+ if ( templateSchema == 0 ) {
+
+ // The schema is not in the template, delete all properties or just all external ones.
+
+ if ( doAll ) {
+
+ workingSchema->RemoveChildren(); // Remove the properties here, delete the schema below.
+
+ } else {
+
+ for ( size_t propOrdinal = workingSchema->children.size(); propOrdinal > 0; --propOrdinal ) {
+ size_t propNum = propOrdinal-1; // ! Convert ordinal to index!
+ XMP_Node * workingProp = workingSchema->children[propNum];
+ if ( IsExternalProperty ( workingSchema->name, workingProp->name ) ) {
+ delete ( workingProp );
+ workingSchema->children.erase ( workingSchema->children.begin() + propNum );
+ }
+ }
+
+ }
+
+ } else {
+
+ // Check each of the working XMP's properties to see if it is in the template.
+
+ for ( size_t propOrdinal = workingSchema->children.size(); propOrdinal > 0; --propOrdinal ) {
+ size_t propNum = propOrdinal-1; // ! Convert ordinal to index!
+ XMP_Node * workingProp = workingSchema->children[propNum];
+ if ( (doAll || IsExternalProperty ( workingSchema->name, workingProp->name )) &&
+ (FindConstChild ( templateSchema, workingProp->name.c_str() ) == 0) ) {
+ delete ( workingProp );
+ workingSchema->children.erase ( workingSchema->children.begin() + propNum );
+ }
+ }
+
+ }
+
+ if ( workingSchema->children.empty() ) {
+ delete ( workingSchema );
+ workingXMP->tree.children.erase ( workingXMP->tree.children.begin() + schemaNum );
+ }
+
+ }
+
+ }
+
+ if ( doAdd | doReplace ) {
+
+ for ( size_t schemaNum = 0, schemaLim = templateXMP.tree.children.size(); schemaNum < schemaLim; ++schemaNum ) {
+
+ const XMP_Node * templateSchema = templateXMP.tree.children[schemaNum];
+
+ // Make sure we have an output schema node, then process the top level template properties.
+
+ XMP_NodePtrPos workingSchemaPos;
+ XMP_Node * workingSchema = FindSchemaNode ( &workingXMP->tree, templateSchema->name.c_str(),
+ kXMP_ExistingOnly, &workingSchemaPos );
+ if ( workingSchema == 0 ) {
+ workingSchema = new XMP_Node ( &workingXMP->tree, templateSchema->name, templateSchema->value, kXMP_SchemaNode );
+ workingXMP->tree.children.push_back ( workingSchema );
+ workingSchemaPos = workingXMP->tree.children.end() - 1;
+ }
+
+ for ( size_t propNum = 0, propLim = templateSchema->children.size(); propNum < propLim; ++propNum ) {
+ const XMP_Node * templateProp = templateSchema->children[propNum];
+ if ( doAll || IsExternalProperty ( templateSchema->name, templateProp->name ) ) {
+ AppendSubtree ( templateProp, workingSchema, doAdd, doReplace, deleteEmpty );
+ }
+ }
+
+ if ( workingSchema->children.empty() ) {
+ delete ( workingSchema );
+ workingXMP->tree.children.erase ( workingSchemaPos );
+ }
+
+ }
+
+ }
+
+} // ApplyTemplate
+
+
+// -------------------------------------------------------------------------------------------------
+// RemoveProperties
+// ----------------
+
+/* class static */ void
+XMPUtils::RemoveProperties ( XMPMeta * xmpObj,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_OptionBits options )
+{
+ XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // ! Enforced by wrapper.
+
+ const bool doAll = XMP_TestOption (options, kXMPUtil_DoAllProperties );
+ const bool includeAliases = XMP_TestOption ( options, kXMPUtil_IncludeAliases );
+
+ if ( *propName != 0 ) {
+
+ // Remove just the one indicated property. This might be an alias, the named schema might
+ // not actually exist. So don't lookup the schema node.
+
+ if ( *schemaNS == 0 ) XMP_Throw ( "Property name requires schema namespace", kXMPErr_BadParam );
+
+ XMP_ExpandedXPath expPath;
+ ExpandXPath ( schemaNS, propName, &expPath );
+
+ XMP_NodePtrPos propPos;
+ XMP_Node * propNode = FindNode ( &(xmpObj->tree), expPath, kXMP_ExistingOnly, kXMP_NoOptions, &propPos );
+ if ( propNode != 0 ) {
+ if ( doAll || IsExternalProperty ( expPath[kSchemaStep].step, expPath[kRootPropStep].step ) ) {
+ XMP_Node * parent = propNode->parent; // *** Should have XMP_Node::RemoveChild(pos).
+ delete propNode; // ! Both delete the node and erase the pointer from the parent.
+ parent->children.erase ( propPos );
+ DeleteEmptySchema ( parent );
+ }
+ }
+
+ } else if ( *schemaNS != 0 ) {
+
+ // Remove all properties from the named schema. Optionally include aliases, in which case
+ // there might not be an actual schema node.
+
+ XMP_NodePtrPos schemaPos;
+ XMP_Node * schemaNode = FindSchemaNode ( &xmpObj->tree, schemaNS, kXMP_ExistingOnly, &schemaPos );
+ if ( schemaNode != 0 ) RemoveSchemaChildren ( schemaPos, doAll );
+
+ if ( includeAliases ) {
+
+ // We're removing the aliases also. Look them up by their namespace prefix. Yes, the
+ // alias map is sorted so we could process just that portion. But that takes more code
+ // and the extra speed isn't worth it. (Plus this way we avoid a dependence on the map
+ // implementation.) Lookup the XMP node from the alias, to make sure the actual exists.
+
+ XMP_StringPtr nsPrefix;
+ XMP_StringLen nsLen;
+ (void) XMPMeta::GetNamespacePrefix ( schemaNS, &nsPrefix, &nsLen );
+
+ XMP_AliasMapPos currAlias = sRegisteredAliasMap->begin();
+ XMP_AliasMapPos endAlias = sRegisteredAliasMap->end();
+
+ for ( ; currAlias != endAlias; ++currAlias ) {
+ if ( strncmp ( currAlias->first.c_str(), nsPrefix, nsLen ) == 0 ) {
+ XMP_NodePtrPos actualPos;
+ XMP_Node * actualProp = FindNode ( &xmpObj->tree, currAlias->second, kXMP_ExistingOnly, kXMP_NoOptions, &actualPos );
+ if ( actualProp != 0 ) {
+ XMP_Node * rootProp = actualProp;
+ while ( ! XMP_NodeIsSchema ( rootProp->parent->options ) ) rootProp = rootProp->parent;
+ if ( doAll || IsExternalProperty ( rootProp->parent->name, rootProp->name ) ) {
+ XMP_Node * parent = actualProp->parent;
+ delete actualProp; // ! Both delete the node and erase the pointer from the parent.
+ parent->children.erase ( actualPos );
+ DeleteEmptySchema ( parent );
+ }
+ }
+ }
+ }
+
+ }
+
+ } else {
+
+ // Remove all appropriate properties from all schema. In this case we don't have to be
+ // concerned with aliases, they are handled implicitly from the actual properties.
+
+ // ! Iterate backwards to reduce shuffling if schema are erased and to simplify the logic
+ // ! for denoting the current schema. (Erasing schema n makes the old n+1 now be n.)
+
+ size_t schemaCount = xmpObj->tree.children.size();
+ XMP_NodePtrPos beginPos = xmpObj->tree.children.begin();
+
+ for ( size_t schemaNum = schemaCount-1, schemaLim = (size_t)(-1); schemaNum != schemaLim; --schemaNum ) {
+ XMP_NodePtrPos currSchema = beginPos + schemaNum;
+ RemoveSchemaChildren ( currSchema, doAll );
+ }
+
+ }
+
+} // RemoveProperties
+
+
+// -------------------------------------------------------------------------------------------------
+// DuplicateSubtree
+// ----------------
+
+/* class static */ void
+XMPUtils::DuplicateSubtree ( const XMPMeta & source,
+ XMPMeta * dest,
+ XMP_StringPtr sourceNS,
+ XMP_StringPtr sourceRoot,
+ XMP_StringPtr destNS,
+ XMP_StringPtr destRoot,
+ XMP_OptionBits options )
+{
+ IgnoreParam(options);
+
+ bool fullSourceTree = false;
+ bool fullDestTree = false;
+
+ XMP_ExpandedXPath sourcePath, destPath;
+
+ const XMP_Node * sourceNode = 0;
+ XMP_Node * destNode = 0;
+
+ XMP_Assert ( (sourceNS != 0) && (*sourceNS != 0) );
+ XMP_Assert ( (sourceRoot != 0) && (*sourceRoot != 0) );
+ XMP_Assert ( (dest != 0) && (destNS != 0) && (destRoot != 0) );
+
+ if ( *destNS == 0 ) destNS = sourceNS;
+ if ( *destRoot == 0 ) destRoot = sourceRoot;
+
+ if ( XMP_LitMatch ( sourceNS, "*" ) ) fullSourceTree = true;
+ if ( XMP_LitMatch ( destNS, "*" ) ) fullDestTree = true;
+
+ if ( (&source == dest) && (fullSourceTree | fullDestTree) ) {
+ XMP_Throw ( "Can't duplicate tree onto itself", kXMPErr_BadParam );
+ }
+
+ if ( fullSourceTree & fullDestTree ) XMP_Throw ( "Use Clone for full tree to full tree", kXMPErr_BadParam );
+
+ if ( fullSourceTree ) {
+
+ // The destination must be an existing empty struct, copy all of the source top level as fields.
+
+ ExpandXPath ( destNS, destRoot, &destPath );
+ destNode = FindNode ( &dest->tree, destPath, kXMP_ExistingOnly );
+
+ if ( (destNode == 0) || (! XMP_PropIsStruct ( destNode->options )) ) {
+ XMP_Throw ( "Destination must be an existing struct", kXMPErr_BadXPath );
+ }
+
+ if ( ! destNode->children.empty() ) {
+ if ( options & kXMP_DeleteExisting ) {
+ destNode->RemoveChildren();
+ } else {
+ XMP_Throw ( "Destination must be an empty struct", kXMPErr_BadXPath );
+ }
+ }
+
+ for ( size_t schemaNum = 0, schemaLim = source.tree.children.size(); schemaNum < schemaLim; ++schemaNum ) {
+
+ const XMP_Node * currSchema = source.tree.children[schemaNum];
+
+ for ( size_t propNum = 0, propLim = currSchema->children.size(); propNum < propLim; ++propNum ) {
+ sourceNode = currSchema->children[propNum];
+ XMP_Node * copyNode = new XMP_Node ( destNode, sourceNode->name, sourceNode->value, sourceNode->options );
+ destNode->children.push_back ( copyNode );
+ CloneOffspring ( sourceNode, copyNode );
+ }
+
+ }
+
+ } else if ( fullDestTree ) {
+
+ // The source node must be an existing struct, copy all of the fields to the dest top level.
+
+ XMP_ExpandedXPath srcPath;
+ ExpandXPath ( sourceNS, sourceRoot, &srcPath );
+ sourceNode = FindConstNode ( &source.tree, srcPath );
+
+ if ( (sourceNode == 0) || (! XMP_PropIsStruct ( sourceNode->options )) ) {
+ XMP_Throw ( "Source must be an existing struct", kXMPErr_BadXPath );
+ }
+
+ destNode = &dest->tree;
+
+ if ( ! destNode->children.empty() ) {
+ if ( options & kXMP_DeleteExisting ) {
+ destNode->RemoveChildren();
+ } else {
+ XMP_Throw ( "Destination tree must be empty", kXMPErr_BadXPath );
+ }
+ }
+
+ std::string nsPrefix;
+ XMP_StringPtr nsURI;
+ XMP_StringLen nsLen;
+
+ for ( size_t fieldNum = 0, fieldLim = sourceNode->children.size(); fieldNum < fieldLim; ++fieldNum ) {
+
+ const XMP_Node * currField = sourceNode->children[fieldNum];
+
+ size_t colonPos = currField->name.find ( ':' );
+ if ( colonPos == std::string::npos ) continue;
+ nsPrefix.assign ( currField->name.c_str(), colonPos );
+ bool nsOK = XMPMeta::GetNamespaceURI ( nsPrefix.c_str(), &nsURI, &nsLen );
+ if ( ! nsOK ) XMP_Throw ( "Source field namespace is not global", kXMPErr_BadSchema );
+
+ XMP_Node * destSchema = FindSchemaNode ( &dest->tree, nsURI, kXMP_CreateNodes );
+ if ( destSchema == 0 ) XMP_Throw ( "Failed to find destination schema", kXMPErr_BadSchema );
+
+ XMP_Node * copyNode = new XMP_Node ( destSchema, currField->name, currField->value, currField->options );
+ destSchema->children.push_back ( copyNode );
+ CloneOffspring ( currField, copyNode );
+
+ }
+
+ } else {
+
+ // Find the root nodes for the source and destination subtrees.
+
+ ExpandXPath ( sourceNS, sourceRoot, &sourcePath );
+ ExpandXPath ( destNS, destRoot, &destPath );
+
+ sourceNode = FindConstNode ( &source.tree, sourcePath );
+ if ( sourceNode == 0 ) XMP_Throw ( "Can't find source subtree", kXMPErr_BadXPath );
+
+ destNode = FindNode ( &dest->tree, destPath, kXMP_ExistingOnly ); // Dest must not yet exist.
+ if ( destNode != 0 ) XMP_Throw ( "Destination subtree must not exist", kXMPErr_BadXPath );
+
+ destNode = FindNode ( &dest->tree, destPath, kXMP_CreateNodes ); // Now create the dest.
+ if ( destNode == 0 ) XMP_Throw ( "Can't create destination root node", kXMPErr_BadXPath );
+
+ // Make sure the destination is not within the source! The source can't be inside the destination
+ // because the source already existed and the destination was just created.
+
+ if ( &source == dest ) {
+ for ( XMP_Node * testNode = destNode; testNode != 0; testNode = testNode->parent ) {
+ if ( testNode == sourceNode ) {
+ // *** delete the just-created dest root node
+ XMP_Throw ( "Destination subtree is within the source subtree", kXMPErr_BadXPath );
+ }
+ }
+ }
+
+ // *** Could use a CloneTree util here and maybe elsewhere.
+
+ destNode->value = sourceNode->value; // *** Should use SetNode.
+ destNode->options = sourceNode->options;
+ CloneOffspring ( sourceNode, destNode );
+
+ }
+
+} // DuplicateSubtree
+
+
+// =================================================================================================
diff --git a/gpr/source/lib/xmp_core/XMPUtils.cpp b/gpr/source/lib/xmp_core/XMPUtils.cpp
new file mode 100644
index 0000000..af65dc9
--- /dev/null
+++ b/gpr/source/lib/xmp_core/XMPUtils.cpp
@@ -0,0 +1,2000 @@
+// =================================================================================================
+// Copyright 2003 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.
+// =================================================================================================
+
+#include "public/include/XMP_Environment.h" // ! This must be the first include!
+#include "XMPCore_Impl.hpp"
+
+#include "XMPUtils.hpp"
+
+#include "md5.h"
+
+#include <map>
+
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <errno.h>
+
+#include <stdio.h> // For snprintf.
+
+#if XMP_WinBuild
+ #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning)
+ #pragma warning ( disable : 4996 ) // '...' was declared deprecated
+#endif
+
+// =================================================================================================
+// Local Types and Constants
+// =========================
+
+static const char * sBase64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+// =================================================================================================
+// Local Utilities
+// ===============
+
+// -------------------------------------------------------------------------------------------------
+// ANSI Time Functions
+// -------------------
+//
+// A bit of hackery to use the best available time functions. Mac, UNIX and iOS have thread safe versions
+// of gmtime and localtime.
+
+#if XMP_MacBuild | XMP_UNIXBuild | XMP_iOSBuild
+
+ typedef time_t ansi_tt;
+ typedef struct tm ansi_tm;
+
+ #define ansi_time time
+ #define ansi_mktime mktime
+ #define ansi_difftime difftime
+
+ #define ansi_gmtime gmtime_r
+ #define ansi_localtime localtime_r
+
+#elif XMP_WinBuild
+
+ // ! VS.Net 2003 (VC7) does not provide thread safe versions of gmtime and localtime.
+ // ! VS.Net 2005 (VC8) inverts the parameters for the safe versions of gmtime and localtime.
+
+ typedef time_t ansi_tt;
+ typedef struct tm ansi_tm;
+
+ #define ansi_time time
+ #define ansi_mktime mktime
+ #define ansi_difftime difftime
+
+ #if defined(_MSC_VER) && (_MSC_VER >= 1400)
+ #define ansi_gmtime(tt,tm) gmtime_s ( tm, tt )
+ #define ansi_localtime(tt,tm) localtime_s ( tm, tt )
+ #else
+ static inline void ansi_gmtime ( const ansi_tt * ttTime, ansi_tm * tmTime )
+ {
+ ansi_tm * tmx = gmtime ( ttTime ); // ! Hope that there is no race!
+ if ( tmx == 0 ) XMP_Throw ( "Failure from ANSI C gmtime function", kXMPErr_ExternalFailure );
+ *tmTime = *tmx;
+ }
+ static inline void ansi_localtime ( const ansi_tt * ttTime, ansi_tm * tmTime )
+ {
+ ansi_tm * tmx = localtime ( ttTime ); // ! Hope that there is no race!
+ if ( tmx == 0 ) XMP_Throw ( "Failure from ANSI C localtime function", kXMPErr_ExternalFailure );
+ *tmTime = *tmx;
+ }
+ #endif
+
+#endif
+
+// -------------------------------------------------------------------------------------------------
+// VerifyDateTimeFlags
+// -------------------
+
+static void
+VerifyDateTimeFlags ( XMP_DateTime * dt )
+{
+
+ if ( (dt->year != 0) || (dt->month != 0) || (dt->day != 0) ) dt->hasDate = true;
+ if ( (dt->hour != 0) || (dt->minute != 0) || (dt->second != 0) || (dt->nanoSecond != 0) ) dt->hasTime = true;
+ if ( (dt->tzSign != 0) || (dt->tzHour != 0) || (dt->tzMinute != 0) ) dt->hasTimeZone = true;
+ if ( dt->hasTimeZone ) dt->hasTime = true; // ! Don't combine with above line, UTC has zero values.
+
+} // VerifyDateTimeFlags
+
+// -------------------------------------------------------------------------------------------------
+// IsLeapYear
+// ----------
+
+static bool
+IsLeapYear ( long year )
+{
+
+ if ( year < 0 ) year = -year + 1; // Fold the negative years, assuming there is a year 0.
+
+ if ( (year % 4) != 0 ) return false; // Not a multiple of 4.
+ if ( (year % 100) != 0 ) return true; // A multiple of 4 but not a multiple of 100.
+ if ( (year % 400) == 0 ) return true; // A multiple of 400.
+
+ return false; // A multiple of 100 but not a multiple of 400.
+
+} // IsLeapYear
+
+// -------------------------------------------------------------------------------------------------
+// DaysInMonth
+// -----------
+
+static int
+DaysInMonth ( XMP_Int32 year, XMP_Int32 month )
+{
+
+ static short daysInMonth[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+ // Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
+
+ int days = daysInMonth [ month ];
+ if ( (month == 2) && IsLeapYear ( year ) ) days += 1;
+
+ return days;
+
+} // DaysInMonth
+
+// -------------------------------------------------------------------------------------------------
+// AdjustTimeOverflow
+// ------------------
+
+static void
+AdjustTimeOverflow ( XMP_DateTime * time )
+{
+ enum { kBillion = 1000*1000*1000L };
+
+ // ----------------------------------------------------------------------------------------------
+ // To be safe against pathalogical overflow we first adjust from month to second, then from
+ // nanosecond back up to month. This leaves each value closer to zero before propagating into it.
+ // For example if the hour and minute are both near max, adjusting minutes first can cause the
+ // hour to overflow.
+
+ // ! Photoshop 8 creates "time only" values with zeros for year, month, and day.
+
+ if ( (time->year != 0) || (time->month != 0) || (time->day != 0) ) {
+
+ while ( time->month < 1 ) {
+ time->year -= 1;
+ time->month += 12;
+ }
+
+ while ( time->month > 12 ) {
+ time->year += 1;
+ time->month -= 12;
+ }
+
+ while ( time->day < 1 ) {
+ time->month -= 1;
+ if ( time->month < 1 ) { // ! Keep the months in range for indexing daysInMonth!
+ time->year -= 1;
+ time->month += 12;
+ }
+ time->day += DaysInMonth ( time->year, time->month ); // ! Decrement month before so index here is right!
+ }
+
+ while ( time->day > DaysInMonth ( time->year, time->month ) ) {
+ time->day -= DaysInMonth ( time->year, time->month ); // ! Increment month after so index here is right!
+ time->month += 1;
+ if ( time->month > 12 ) {
+ time->year += 1;
+ time->month -= 12;
+ }
+ }
+
+ }
+
+ while ( time->hour < 0 ) {
+ time->day -= 1;
+ time->hour += 24;
+ }
+
+ while ( time->hour >= 24 ) {
+ time->day += 1;
+ time->hour -= 24;
+ }
+
+ while ( time->minute < 0 ) {
+ time->hour -= 1;
+ time->minute += 60;
+ }
+
+ while ( time->minute >= 60 ) {
+ time->hour += 1;
+ time->minute -= 60;
+ }
+
+ while ( time->second < 0 ) {
+ time->minute -= 1;
+ time->second += 60;
+ }
+
+ while ( time->second >= 60 ) {
+ time->minute += 1;
+ time->second -= 60;
+ }
+
+ while ( time->nanoSecond < 0 ) {
+ time->second -= 1;
+ time->nanoSecond += kBillion;
+ }
+
+ while ( time->nanoSecond >= kBillion ) {
+ time->second += 1;
+ time->nanoSecond -= kBillion;
+ }
+
+ while ( time->second < 0 ) {
+ time->minute -= 1;
+ time->second += 60;
+ }
+
+ while ( time->second >= 60 ) {
+ time->minute += 1;
+ time->second -= 60;
+ }
+
+ while ( time->minute < 0 ) {
+ time->hour -= 1;
+ time->minute += 60;
+ }
+
+ while ( time->minute >= 60 ) {
+ time->hour += 1;
+ time->minute -= 60;
+ }
+
+ while ( time->hour < 0 ) {
+ time->day -= 1;
+ time->hour += 24;
+ }
+
+ while ( time->hour >= 24 ) {
+ time->day += 1;
+ time->hour -= 24;
+ }
+
+ if ( (time->year != 0) || (time->month != 0) || (time->day != 0) ) {
+
+ while ( time->month < 1 ) { // Make sure the months are OK first, for DaysInMonth.
+ time->year -= 1;
+ time->month += 12;
+ }
+
+ while ( time->month > 12 ) {
+ time->year += 1;
+ time->month -= 12;
+ }
+
+ while ( time->day < 1 ) {
+ time->month -= 1;
+ if ( time->month < 1 ) {
+ time->year -= 1;
+ time->month += 12;
+ }
+ time->day += DaysInMonth ( time->year, time->month );
+ }
+
+ while ( time->day > DaysInMonth ( time->year, time->month ) ) {
+ time->day -= DaysInMonth ( time->year, time->month );
+ time->month += 1;
+ if ( time->month > 12 ) {
+ time->year += 1;
+ time->month -= 12;
+ }
+ }
+
+ }
+
+} // AdjustTimeOverflow
+
+// -------------------------------------------------------------------------------------------------
+// GatherInt
+// ---------
+//
+// Gather into a 64-bit value in order to easily check for overflow. Using a 32-bit value and
+// checking for negative isn't reliable, the "*10" part can wrap around to a low positive value.
+
+static XMP_Int32
+GatherInt ( XMP_StringPtr strValue, size_t * _pos, const char * errMsg )
+{
+ size_t pos = *_pos;
+ XMP_Int64 value = 0;
+
+ enum { kMaxSInt32 = 0x7FFFFFFF };
+
+ for ( char ch = strValue[pos]; ('0' <= ch) && (ch <= '9'); ++pos, ch = strValue[pos] ) {
+ value = (value * 10) + (ch - '0');
+ if ( value > kMaxSInt32 ) XMP_Throw ( errMsg, kXMPErr_BadValue );
+ }
+
+ if ( pos == *_pos ) XMP_Throw ( errMsg, kXMPErr_BadParam );
+ *_pos = pos;
+ return (XMP_Int32)value;
+
+} // GatherInt
+
+// -------------------------------------------------------------------------------------------------
+
+static void FormatFullDateTime ( XMP_DateTime & tempDate, char * buffer, size_t bufferLen )
+{
+
+ AdjustTimeOverflow ( &tempDate ); // Make sure all time parts are in range.
+
+ if ( (tempDate.second == 0) && (tempDate.nanoSecond == 0) ) {
+
+ // Output YYYY-MM-DDThh:mmTZD.
+ snprintf ( buffer, bufferLen, "%.4d-%02d-%02dT%02d:%02d", // AUDIT: Callers pass sizeof(buffer).
+ tempDate.year, tempDate.month, tempDate.day, tempDate.hour, tempDate.minute );
+
+ } else if ( tempDate.nanoSecond == 0 ) {
+
+ // Output YYYY-MM-DDThh:mm:ssTZD.
+ snprintf ( buffer, bufferLen, "%.4d-%02d-%02dT%02d:%02d:%02d", // AUDIT: Callers pass sizeof(buffer).
+ tempDate.year, tempDate.month, tempDate.day,
+ tempDate.hour, tempDate.minute, tempDate.second );
+
+ } else {
+
+ // Output YYYY-MM-DDThh:mm:ss.sTZD.
+ snprintf ( buffer, bufferLen, "%.4d-%02d-%02dT%02d:%02d:%02d.%09d", // AUDIT: Callers pass sizeof(buffer).
+ tempDate.year, tempDate.month, tempDate.day,
+ tempDate.hour, tempDate.minute, tempDate.second, tempDate.nanoSecond );
+ buffer[bufferLen - 1] = 0; // AUDIT warning C6053: make sure string is terminated. buffer is already filled with 0 from caller
+ for ( size_t i = strlen(buffer)-1; buffer[i] == '0'; --i ) buffer[i] = 0; // Trim excess digits.
+ }
+
+} // FormatFullDateTime
+
+// -------------------------------------------------------------------------------------------------
+// DecodeBase64Char
+// ----------------
+
+// The decode mapping:
+//
+// encoded encoded raw
+// char value value
+// ------- ------- -----
+// A .. Z 0x41 .. 0x5A 0 .. 25
+// a .. z 0x61 .. 0x7A 26 .. 51
+// 0 .. 9 0x30 .. 0x39 52 .. 61
+// + 0x2B 62
+// / 0x2F 63
+
+static unsigned char
+DecodeBase64Char ( XMP_Uns8 ch )
+{
+
+ if ( ('A' <= ch) && (ch <= 'Z') ) {
+ ch = ch - 'A';
+ } else if ( ('a' <= ch) && (ch <= 'z') ) {
+ ch = ch - 'a' + 26;
+ } else if ( ('0' <= ch) && (ch <= '9') ) {
+ ch = ch - '0' + 52;
+ } else if ( ch == '+' ) {
+ ch = 62;
+ } else if ( ch == '/' ) {
+ ch = 63;
+ } else if ( (ch == ' ') || (ch == kTab) || (ch == kLF) || (ch == kCR) ) {
+ ch = 0xFF; // Will be ignored by the caller.
+ } else {
+ XMP_Throw ( "Invalid base-64 encoded character", kXMPErr_BadParam );
+ }
+
+ return ch;
+
+} // DecodeBase64Char ();
+
+// -------------------------------------------------------------------------------------------------
+// EstimateSizeForJPEG
+// -------------------
+//
+// Estimate the serialized size for the subtree of an XMP_Node. Support for PackageForJPEG.
+
+static size_t
+EstimateSizeForJPEG ( const XMP_Node * xmpNode )
+{
+
+ size_t estSize = 0;
+ size_t nameSize = xmpNode->name.size();
+ bool includeName = (! XMP_PropIsArray ( xmpNode->parent->options ));
+
+ if ( XMP_PropIsSimple ( xmpNode->options ) ) {
+
+ if ( includeName ) estSize += (nameSize + 3); // Assume attribute form.
+ estSize += xmpNode->value.size();
+
+ } else if ( XMP_PropIsArray ( xmpNode->options ) ) {
+
+ // The form of the value portion is: <rdf:Xyz><rdf:li>...</rdf:li>...</rdf:Xyx>
+ if ( includeName ) estSize += (2*nameSize + 5);
+ size_t arraySize = xmpNode->children.size();
+ estSize += 9 + 10; // The rdf:Xyz tags.
+ estSize += arraySize * (8 + 9); // The rdf:li tags.
+ for ( size_t i = 0; i < arraySize; ++i ) {
+ estSize += EstimateSizeForJPEG ( xmpNode->children[i] );
+ }
+
+ } else {
+
+ // The form is: <headTag rdf:parseType="Resource">...fields...</tailTag>
+ if ( includeName ) estSize += (2*nameSize + 5);
+ estSize += 25; // The rdf:parseType="Resource" attribute.
+ size_t fieldCount = xmpNode->children.size();
+ for ( size_t i = 0; i < fieldCount; ++i ) {
+ estSize += EstimateSizeForJPEG ( xmpNode->children[i] );
+ }
+
+ }
+
+ return estSize;
+
+} // EstimateSizeForJPEG
+
+// -------------------------------------------------------------------------------------------------
+// MoveOneProperty
+// ---------------
+
+static bool MoveOneProperty ( XMPMeta & stdXMP, XMPMeta * extXMP,
+ XMP_StringPtr schemaURI, XMP_StringPtr propName )
+{
+
+ XMP_Node * propNode = 0;
+ XMP_NodePtrPos stdPropPos;
+
+ XMP_Node * stdSchema = FindSchemaNode ( &stdXMP.tree, schemaURI, kXMP_ExistingOnly, 0 );
+ if ( stdSchema != 0 ) {
+ propNode = FindChildNode ( stdSchema, propName, kXMP_ExistingOnly, &stdPropPos );
+ }
+ if ( propNode == 0 ) return false;
+
+ XMP_Node * extSchema = FindSchemaNode ( &extXMP->tree, schemaURI, kXMP_CreateNodes );
+
+ propNode->parent = extSchema;
+
+ extSchema->options &= ~kXMP_NewImplicitNode;
+ extSchema->children.push_back ( propNode );
+
+ stdSchema->children.erase ( stdPropPos );
+ DeleteEmptySchema ( stdSchema );
+
+ return true;
+
+} // MoveOneProperty
+
+// -------------------------------------------------------------------------------------------------
+// CreateEstimatedSizeMap
+// ----------------------
+
+#ifndef Trace_PackageForJPEG
+ #define Trace_PackageForJPEG 0
+#endif
+
+typedef std::pair < XMP_VarString*, XMP_VarString* > StringPtrPair;
+typedef std::multimap < size_t, StringPtrPair > PropSizeMap;
+
+static void CreateEstimatedSizeMap ( XMPMeta & stdXMP, PropSizeMap * propSizes )
+{
+ #if Trace_PackageForJPEG
+ printf ( " Creating top level property map:\n" );
+ #endif
+
+ for ( size_t s = stdXMP.tree.children.size(); s > 0; --s ) {
+
+ XMP_Node * stdSchema = stdXMP.tree.children[s-1];
+
+ for ( size_t p = stdSchema->children.size(); p > 0; --p ) {
+
+ XMP_Node * stdProp = stdSchema->children[p-1];
+ if ( (stdSchema->name == kXMP_NS_XMP_Note) &&
+ (stdProp->name == "xmpNote:HasExtendedXMP") ) continue; // ! Don't move xmpNote:HasExtendedXMP.
+
+ size_t propSize = EstimateSizeForJPEG ( stdProp );
+ StringPtrPair namePair ( &stdSchema->name, &stdProp->name );
+ PropSizeMap::value_type mapValue ( propSize, namePair );
+
+ (void) propSizes->insert ( propSizes->upper_bound ( propSize ), mapValue );
+ #if Trace_PackageForJPEG
+ printf ( " %d bytes, %s in %s\n", propSize, stdProp->name.c_str(), stdSchema->name.c_str() );
+ #endif
+
+ }
+
+ }
+
+} // CreateEstimatedSizeMap
+
+// -------------------------------------------------------------------------------------------------
+// MoveLargestProperty
+// -------------------
+
+static size_t MoveLargestProperty ( XMPMeta & stdXMP, XMPMeta * extXMP, PropSizeMap & propSizes )
+{
+ XMP_Assert ( ! propSizes.empty() );
+
+ #if 0
+ // *** Xocde 2.3 on Mac OS X 10.4.7 seems to have a bug where this does not pick the last
+ // *** item in the map. We'll just avoid it on all platforms until thoroughly tested.
+ PropSizeMap::iterator lastPos = propSizes.end();
+ --lastPos; // Move to the actual last item.
+ #else
+ PropSizeMap::iterator lastPos = propSizes.begin();
+ PropSizeMap::iterator nextPos = lastPos;
+ for ( ++nextPos; nextPos != propSizes.end(); ++nextPos ) lastPos = nextPos;
+ #endif
+
+ size_t propSize = lastPos->first;
+ const char * schemaURI = lastPos->second.first->c_str();
+ const char * propName = lastPos->second.second->c_str();
+
+ #if Trace_PackageForJPEG
+ printf ( " Move %s, %d bytes\n", propName, propSize );
+ #endif
+
+ bool moved = MoveOneProperty ( stdXMP, extXMP, schemaURI, propName );
+ XMP_Assert ( moved );
+
+ propSizes.erase ( lastPos );
+ return propSize;
+
+} // MoveLargestProperty
+
+// =================================================================================================
+// Class Static Functions
+// ======================
+
+// -------------------------------------------------------------------------------------------------
+// Initialize
+// ----------
+
+/* class static */ bool
+XMPUtils::Initialize()
+{
+
+ if ( WhiteSpaceStrPtr == NULL ) {
+ WhiteSpaceStrPtr = new std::string();
+ WhiteSpaceStrPtr->append( " \t\n\r" );
+ }
+ return true;
+
+} // Initialize
+
+// -------------------------------------------------------------------------------------------------
+// Terminate
+// ---------
+
+/* class static */ void
+XMPUtils::Terminate() RELEASE_NO_THROW
+{
+
+ delete WhiteSpaceStrPtr;
+ WhiteSpaceStrPtr = NULL;
+ return;
+
+} // Terminate
+
+// -------------------------------------------------------------------------------------------------
+// ComposeArrayItemPath
+// --------------------
+//
+// Return "arrayName[index]".
+
+/* class static */ void
+XMPUtils::ComposeArrayItemPath ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex,
+ XMP_VarString * _fullPath )
+{
+ XMP_Assert ( schemaNS != 0 ); // Enforced by wrapper.
+ XMP_Assert ( (arrayName != 0) && (*arrayName != 0) ); // Enforced by wrapper.
+ XMP_Assert ( _fullPath != 0 ); // Enforced by wrapper.
+
+ XMP_ExpandedXPath expPath; // Just for side effects to check namespace and basic path.
+ ExpandXPath ( schemaNS, arrayName, &expPath );
+
+ if ( (itemIndex < 0) && (itemIndex != kXMP_ArrayLastItem) ) XMP_Throw ( "Array index out of bounds", kXMPErr_BadParam );
+
+ XMP_StringLen reserveLen = strlen(arrayName) + 2 + 32; // Room plus padding.
+
+ XMP_VarString fullPath; // ! Allow for arrayName to be the incoming _fullPath.c_str().
+ fullPath.reserve ( reserveLen );
+ fullPath = arrayName;
+
+ if ( itemIndex == kXMP_ArrayLastItem ) {
+ fullPath += "[last()]";
+ } else {
+ // AUDIT: Using sizeof(buffer) for the snprintf length is safe.
+ char buffer [32]; // A 32 byte buffer is plenty, even for a 64-bit integer.
+ snprintf ( buffer, sizeof(buffer), "[%d]", itemIndex );
+ fullPath += buffer;
+ }
+
+ *_fullPath = fullPath;
+
+} // ComposeArrayItemPath
+
+// -------------------------------------------------------------------------------------------------
+// ComposeStructFieldPath
+// ----------------------
+//
+// Return "structName/ns:fieldName".
+
+/* class static */ void
+XMPUtils::ComposeStructFieldPath ( XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ XMP_VarString * _fullPath )
+{
+ XMP_Assert ( (schemaNS != 0) && (fieldNS != 0) ); // Enforced by wrapper.
+ XMP_Assert ( (structName != 0) && (*structName != 0) ); // Enforced by wrapper.
+ XMP_Assert ( (fieldName != 0) && (*fieldName != 0) ); // Enforced by wrapper.
+ XMP_Assert ( _fullPath != 0 ); // Enforced by wrapper.
+
+ XMP_ExpandedXPath expPath; // Just for side effects to check namespace and basic path.
+ ExpandXPath ( schemaNS, structName, &expPath );
+
+ XMP_ExpandedXPath fieldPath;
+ ExpandXPath ( fieldNS, fieldName, &fieldPath );
+ if ( fieldPath.size() != 2 ) XMP_Throw ( "The fieldName must be simple", kXMPErr_BadXPath );
+
+ XMP_StringLen reserveLen = strlen(structName) + fieldPath[kRootPropStep].step.size() + 1;
+
+ XMP_VarString fullPath; // ! Allow for arrayName to be the incoming _fullPath.c_str().
+ fullPath.reserve ( reserveLen );
+ fullPath = structName;
+ fullPath += '/';
+ fullPath += fieldPath[kRootPropStep].step;
+
+ *_fullPath = fullPath;
+
+} // ComposeStructFieldPath
+
+// -------------------------------------------------------------------------------------------------
+// ComposeQualifierPath
+// --------------------
+//
+// Return "propName/?ns:qualName".
+
+/* class static */ void
+XMPUtils::ComposeQualifierPath ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName,
+ XMP_VarString * _fullPath )
+{
+ XMP_Assert ( (schemaNS != 0) && (qualNS != 0) ); // Enforced by wrapper.
+ XMP_Assert ( (propName != 0) && (*propName != 0) ); // Enforced by wrapper.
+ XMP_Assert ( (qualName != 0) && (*qualName != 0) ); // Enforced by wrapper.
+ XMP_Assert ( _fullPath != 0 ); // Enforced by wrapper.
+
+ XMP_ExpandedXPath expPath; // Just for side effects to check namespace and basic path.
+ ExpandXPath ( schemaNS, propName, &expPath );
+
+ XMP_ExpandedXPath qualPath;
+ ExpandXPath ( qualNS, qualName, &qualPath );
+ if ( qualPath.size() != 2 ) XMP_Throw ( "The qualifier name must be simple", kXMPErr_BadXPath );
+
+ XMP_StringLen reserveLen = strlen(propName) + qualPath[kRootPropStep].step.size() + 2;
+
+ XMP_VarString fullPath; // ! Allow for arrayName to be the incoming _fullPath.c_str().
+ fullPath.reserve ( reserveLen );
+ fullPath = propName;
+ fullPath += "/?";
+ fullPath += qualPath[kRootPropStep].step;
+
+ *_fullPath = fullPath;
+
+} // ComposeQualifierPath
+
+// -------------------------------------------------------------------------------------------------
+// ComposeLangSelector
+// -------------------
+//
+// Return "arrayName[?xml:lang="lang"]".
+
+// *** #error "handle quotes in the lang - or verify format"
+
+/* class static */ void
+XMPUtils::ComposeLangSelector ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr _langName,
+ XMP_VarString * _fullPath )
+{
+ XMP_Assert ( schemaNS != 0 ); // Enforced by wrapper.
+ XMP_Assert ( (arrayName != 0) && (*arrayName != 0) ); // Enforced by wrapper.
+ XMP_Assert ( (_langName != 0) && (*_langName != 0) ); // Enforced by wrapper.
+ XMP_Assert ( _fullPath != 0 ); // Enforced by wrapper.
+
+ XMP_ExpandedXPath expPath; // Just for side effects to check namespace and basic path.
+ ExpandXPath ( schemaNS, arrayName, &expPath );
+
+ XMP_VarString langName ( _langName );
+ NormalizeLangValue ( &langName );
+
+ XMP_StringLen reserveLen = strlen(arrayName) + langName.size() + 14;
+
+ XMP_VarString fullPath; // ! Allow for arrayName to be the incoming _fullPath.c_str().
+ fullPath.reserve ( reserveLen );
+ fullPath = arrayName;
+ fullPath += "[?xml:lang=\"";
+ fullPath += langName;
+ fullPath += "\"]";
+
+ *_fullPath = fullPath;
+
+} // ComposeLangSelector
+
+// -------------------------------------------------------------------------------------------------
+// ComposeFieldSelector
+// --------------------
+//
+// Return "arrayName[ns:fieldName="fieldValue"]".
+
+// *** #error "handle quotes in the value"
+
+/* class static */ void
+XMPUtils::ComposeFieldSelector ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ XMP_StringPtr fieldValue,
+ XMP_VarString * _fullPath )
+{
+ XMP_Assert ( (schemaNS != 0) && (fieldNS != 0) && (fieldValue != 0) ); // Enforced by wrapper.
+ XMP_Assert ( (*arrayName != 0) && (*fieldName != 0) ); // Enforced by wrapper.
+ XMP_Assert ( _fullPath != 0 ); // Enforced by wrapper.
+
+ XMP_ExpandedXPath expPath; // Just for side effects to check namespace and basic path.
+ ExpandXPath ( schemaNS, arrayName, &expPath );
+
+ XMP_ExpandedXPath fieldPath;
+ ExpandXPath ( fieldNS, fieldName, &fieldPath );
+ if ( fieldPath.size() != 2 ) XMP_Throw ( "The fieldName must be simple", kXMPErr_BadXPath );
+
+ XMP_StringLen reserveLen = strlen(arrayName) + fieldPath[kRootPropStep].step.size() + strlen(fieldValue) + 5;
+
+ XMP_VarString fullPath; // ! Allow for arrayName to be the incoming _fullPath.c_str().
+ fullPath.reserve ( reserveLen );
+ fullPath = arrayName;
+ fullPath += '[';
+ fullPath += fieldPath[kRootPropStep].step;
+ fullPath += "=\"";
+ fullPath += fieldValue;
+ fullPath += "\"]";
+
+ *_fullPath = fullPath;
+
+} // ComposeFieldSelector
+
+// -------------------------------------------------------------------------------------------------
+// ConvertFromBool
+// ---------------
+
+/* class static */ void
+XMPUtils::ConvertFromBool ( bool binValue,
+ XMP_VarString * strValue )
+{
+ XMP_Assert ( strValue != 0 ); // Enforced by wrapper.
+
+ if ( binValue ) {
+ *strValue = kXMP_TrueStr;
+ } else {
+ *strValue = kXMP_FalseStr;
+ }
+
+} // ConvertFromBool
+
+// -------------------------------------------------------------------------------------------------
+// ConvertFromInt
+// --------------
+
+/* class static */ void
+XMPUtils::ConvertFromInt ( XMP_Int32 binValue,
+ XMP_StringPtr format,
+ XMP_VarString * strValue )
+{
+ XMP_Assert ( (format != 0) && (strValue != 0) ); // Enforced by wrapper.
+
+ strValue->erase();
+ if ( *format == 0 ) format = "%d";
+
+ // AUDIT: Using sizeof(buffer) for the snprintf length is safe.
+ char buffer [32]; // Big enough for a 64-bit integer;
+ snprintf ( buffer, sizeof(buffer), format, binValue );
+
+ *strValue = buffer;
+
+} // ConvertFromInt
+
+// -------------------------------------------------------------------------------------------------
+// ConvertFromInt64
+// ----------------
+
+/* class static */ void
+XMPUtils::ConvertFromInt64 ( XMP_Int64 binValue,
+ XMP_StringPtr format,
+ XMP_VarString * strValue )
+{
+ XMP_Assert ( (format != 0) && (strValue != 0) ); // Enforced by wrapper.
+
+ strValue->erase();
+ if ( *format == 0 ) format = "%lld";
+
+ // AUDIT: Using sizeof(buffer) for the snprintf length is safe.
+ char buffer [32]; // Big enough for a 64-bit integer;
+ snprintf ( buffer, sizeof(buffer), format, binValue );
+
+ *strValue = buffer;
+
+} // ConvertFromInt64
+
+// -------------------------------------------------------------------------------------------------
+// ConvertFromFloat
+// ----------------
+
+/* class static */ void
+XMPUtils::ConvertFromFloat ( double binValue,
+ XMP_StringPtr format,
+ XMP_VarString * strValue )
+{
+ XMP_Assert ( (format != 0) && (strValue != 0) ); // Enforced by wrapper.
+
+ strValue->erase();
+ if ( *format == 0 ) format = "%f";
+
+ // AUDIT: Using sizeof(buffer) for the snprintf length is safe.
+ char buffer [64]; // Ought to be plenty big enough.
+ snprintf ( buffer, sizeof(buffer), format, binValue );
+
+ *strValue = buffer;
+
+} // ConvertFromFloat
+
+// -------------------------------------------------------------------------------------------------
+// ConvertFromDate
+// ---------------
+//
+// Format a date-time string according to ISO 8601 and http://www.w3.org/TR/NOTE-datetime:
+// YYYY
+// YYYY-MM
+// YYYY-MM-DD
+// YYYY-MM-DDThh:mmTZD
+// YYYY-MM-DDThh:mm:ssTZD
+// YYYY-MM-DDThh:mm:ss.sTZD
+//
+// YYYY = four-digit year
+// MM = two-digit month (01=January, etc.)
+// DD = two-digit day of month (01 through 31)
+// hh = two digits of hour (00 through 23)
+// mm = two digits of minute (00 through 59)
+// ss = two digits of second (00 through 59)
+// s = one or more digits representing a decimal fraction of a second
+// TZD = time zone designator (Z or +hh:mm or -hh:mm)
+//
+// Note that ISO 8601 does not seem to allow years less than 1000 or greater than 9999. We allow
+// any year, even negative ones. The year is formatted as "%.4d". The TZD is also optional in XMP,
+// even though required in the W3C profile. Finally, Photoshop 8 (CS) sometimes created time-only
+// values so we tolerate that.
+
+/* class static */ void
+XMPUtils::ConvertFromDate ( const XMP_DateTime & _inValue,
+ XMP_VarString * strValue )
+{
+ XMP_Assert ( strValue != 0 ); // Enforced by wrapper.
+
+ char buffer [100]; // Plenty long enough.
+ memset( buffer, 0, 100);
+
+ // Pick the format, use snprintf to format into a local buffer, assign to static output string.
+ // Don't use AdjustTimeOverflow at the start, that will wipe out zero month or day values.
+
+ // ! Photoshop 8 creates "time only" values with zeros for year, month, and day.
+
+ XMP_DateTime binValue = _inValue;
+ VerifyDateTimeFlags ( &binValue );
+
+ // Temporary fix for bug 1269463, silently fix out of range month or day.
+
+ if ( binValue.month == 0 ) {
+ if ( (binValue.day != 0) || binValue.hasTime ) binValue.month = 1;
+ } else {
+ if ( binValue.month < 1 ) binValue.month = 1;
+ if ( binValue.month > 12 ) binValue.month = 12;
+ }
+
+ if ( binValue.day == 0 ) {
+ if ( binValue.hasTime ) binValue.day = 1;
+ } else {
+ if ( binValue.day < 1 ) binValue.day = 1;
+ if ( binValue.day > 31 ) binValue.day = 31;
+ }
+
+ // Now carry on with the original logic.
+
+ if ( binValue.month == 0 ) {
+
+ // Output YYYY if all else is zero, otherwise output a full string for the quasi-bogus
+ // "time only" values from Photoshop CS.
+ if ( (binValue.day == 0) && (! binValue.hasTime) ) {
+ snprintf ( buffer, sizeof(buffer), "%.4d", binValue.year ); // AUDIT: Using sizeof for snprintf length is safe.
+ } else if ( (binValue.year == 0) && (binValue.day == 0) ) {
+ FormatFullDateTime ( binValue, buffer, sizeof(buffer) );
+ } else {
+ XMP_Throw ( "Invalid partial date", kXMPErr_BadParam);
+ }
+
+ } else if ( binValue.day == 0 ) {
+
+ // Output YYYY-MM.
+ if ( (binValue.month < 1) || (binValue.month > 12) ) XMP_Throw ( "Month is out of range", kXMPErr_BadParam);
+ if ( binValue.hasTime ) XMP_Throw ( "Invalid partial date, non-zeros after zero month and day", kXMPErr_BadParam);
+ snprintf ( buffer, sizeof(buffer), "%.4d-%02d", binValue.year, binValue.month ); // AUDIT: Using sizeof for snprintf length is safe.
+
+ } else if ( ! binValue.hasTime ) {
+
+ // Output YYYY-MM-DD.
+ if ( (binValue.month < 1) || (binValue.month > 12) ) XMP_Throw ( "Month is out of range", kXMPErr_BadParam);
+ if ( (binValue.day < 1) || (binValue.day > 31) ) XMP_Throw ( "Day is out of range", kXMPErr_BadParam);
+ snprintf ( buffer, sizeof(buffer), "%.4d-%02d-%02d", binValue.year, binValue.month, binValue.day ); // AUDIT: Using sizeof for snprintf length is safe.
+
+ } else {
+
+ FormatFullDateTime ( binValue, buffer, sizeof(buffer) );
+
+ }
+
+ strValue->assign ( buffer );
+
+ if ( binValue.hasTimeZone ) {
+
+ if ( (binValue.tzHour < 0) || (binValue.tzHour > 23) ||
+ (binValue.tzMinute < 0 ) || (binValue.tzMinute > 59) ||
+ (binValue.tzSign < -1) || (binValue.tzSign > +1) ||
+ ((binValue.tzSign == 0) && ((binValue.tzHour != 0) || (binValue.tzMinute != 0))) ) {
+ XMP_Throw ( "Invalid time zone values", kXMPErr_BadParam );
+ }
+
+ if ( binValue.tzSign == 0 ) {
+ *strValue += 'Z';
+ } else {
+ snprintf ( buffer, sizeof(buffer), "+%02d:%02d", binValue.tzHour, binValue.tzMinute ); // AUDIT: Using sizeof for snprintf length is safe.
+ if ( binValue.tzSign < 0 ) buffer[0] = '-';
+ *strValue += buffer;
+ }
+
+ }
+
+} // ConvertFromDate
+
+// -------------------------------------------------------------------------------------------------
+// ConvertToBool
+// -------------
+//
+// Formally the string value should be "True" or "False", but we should be more flexible here. Map
+// the string to lower case. Allow any of "true", "false", "t", "f", "1", or "0".
+
+/* class static */ bool
+XMPUtils::ConvertToBool ( XMP_StringPtr strValue )
+{
+ if ( (strValue == 0) || (*strValue == 0) ) XMP_Throw ( "Empty convert-from string", kXMPErr_BadValue );
+
+ bool result = false;
+ XMP_VarString strObj ( strValue );
+
+ for ( XMP_VarStringPos ch = strObj.begin(); ch != strObj.end(); ++ch ) {
+ if ( ('A' <= *ch) && (*ch <= 'Z') ) *ch += 0x20;
+ }
+
+ if ( (strObj == "true") || (strObj == "t") || (strObj == "1") ) {
+ result = true;
+ } else if ( (strObj == "false") || (strObj == "f") || (strObj == "0") ) {
+ result = false;
+ } else {
+ XMP_Throw ( "Invalid Boolean string", kXMPErr_BadParam );
+ }
+
+ return result;
+
+} // ConvertToBool
+
+// -------------------------------------------------------------------------------------------------
+// ConvertToInt
+// ------------
+
+/* class static */ XMP_Int32
+XMPUtils::ConvertToInt ( XMP_StringPtr strValue )
+{
+ if ( (strValue == 0) || (*strValue == 0) ) XMP_Throw ( "Empty convert-from string", kXMPErr_BadValue );
+
+ int count;
+ char nextCh;
+ XMP_Int32 result;
+
+ if ( ! XMP_LitNMatch ( strValue, "0x", 2 ) ) {
+ count = sscanf ( strValue, "%d%c", &result, &nextCh );
+ } else {
+ count = sscanf ( strValue, "%x%c", &result, &nextCh );
+ }
+
+ if ( count != 1 ) XMP_Throw ( "Invalid integer string", kXMPErr_BadParam );
+
+ return result;
+
+} // ConvertToInt
+
+// -------------------------------------------------------------------------------------------------
+// ConvertToInt64
+// --------------
+
+/* class static */ XMP_Int64
+XMPUtils::ConvertToInt64 ( XMP_StringPtr strValue )
+{
+ if ( (strValue == 0) || (*strValue == 0) ) XMP_Throw ( "Empty convert-from string", kXMPErr_BadValue );
+
+ int count;
+ char nextCh;
+ XMP_Int64 result;
+
+ if ( ! XMP_LitNMatch ( strValue, "0x", 2 ) ) {
+ count = sscanf ( strValue, "%lld%c", &result, &nextCh );
+ } else {
+ count = sscanf ( strValue, "%llx%c", &result, &nextCh );
+ }
+
+ if ( count != 1 ) XMP_Throw ( "Invalid integer string", kXMPErr_BadParam );
+
+ return result;
+
+} // ConvertToInt64
+
+// -------------------------------------------------------------------------------------------------
+// ConvertToFloat
+// --------------
+
+/* class static */ double
+XMPUtils::ConvertToFloat ( XMP_StringPtr strValue )
+{
+ if ( (strValue == 0) || (*strValue == 0) ) XMP_Throw ( "Empty convert-from string", kXMPErr_BadValue );
+
+ XMP_VarString oldLocale; // Try to make sure number conversion uses '.' as the decimal point.
+ XMP_StringPtr oldLocalePtr = setlocale ( LC_ALL, 0 );
+ if ( oldLocalePtr != 0 ) {
+ oldLocale.assign ( oldLocalePtr ); // Save the locale to be reset when exiting.
+ setlocale ( LC_ALL, "C" );
+ }
+
+ errno = 0;
+ char * numEnd;
+ double result = strtod ( strValue, &numEnd );
+ int errnoSave = errno; // The setlocale call below might change errno.
+
+ if ( ! oldLocale.empty() ) setlocale ( LC_ALL, oldLocale.c_str() ); // ! Reset locale before possible throw!
+ if ( (errnoSave != 0) || (*numEnd != 0) ) XMP_Throw ( "Invalid float string", kXMPErr_BadParam );
+
+ return result;
+
+} // ConvertToFloat
+
+// -------------------------------------------------------------------------------------------------
+// ConvertToDate
+// -------------
+//
+// Parse a date-time string according to ISO 8601 and http://www.w3.org/TR/NOTE-datetime:
+// YYYY
+// YYYY-MM
+// YYYY-MM-DD
+// YYYY-MM-DDThh:mmTZD
+// YYYY-MM-DDThh:mm:ssTZD
+// YYYY-MM-DDThh:mm:ss.sTZD
+//
+// YYYY = four-digit year
+// MM = two-digit month (01=January, etc.)
+// DD = two-digit day of month (01 through 31)
+// hh = two digits of hour (00 through 23)
+// mm = two digits of minute (00 through 59)
+// ss = two digits of second (00 through 59)
+// s = one or more digits representing a decimal fraction of a second
+// TZD = time zone designator (Z or +hh:mm or -hh:mm)
+//
+// Note that ISO 8601 does not seem to allow years less than 1000 or greater than 9999. We allow
+// any year, even negative ones. The year is formatted as "%.4d". The TZD is also optional in XMP,
+// even though required in the W3C profile. Finally, Photoshop 8 (CS) sometimes created time-only
+// values so we tolerate that.
+
+// *** Put the ISO format comments in the header documentation.
+
+/* class static */ void
+XMPUtils::ConvertToDate ( XMP_StringPtr strValue,
+ XMP_DateTime * binValue )
+{
+ if ( (strValue == 0) || (*strValue == 0) ) XMP_Throw ( "Empty convert-from string", kXMPErr_BadValue);
+
+ size_t pos = 0;
+ XMP_Int32 temp;
+
+ XMP_Assert ( sizeof(*binValue) == sizeof(XMP_DateTime) );
+ (void) memset ( binValue, 0, sizeof(*binValue) ); // AUDIT: Safe, using sizeof destination.
+
+ size_t strSize = strlen ( strValue );
+ bool timeOnly = ( (strValue[0] == 'T') ||
+ ((strSize >= 2) && (strValue[1] == ':')) ||
+ ((strSize >= 3) && (strValue[2] == ':')) );
+
+ if ( ! timeOnly ) {
+
+ binValue->hasDate = true;
+
+ if ( strValue[0] == '-' ) pos = 1;
+
+ temp = GatherInt ( strValue, &pos, "Invalid year in date string" ); // Extract the year.
+ if ( (strValue[pos] != 0) && (strValue[pos] != '-') ) XMP_Throw ( "Invalid date string, after year", kXMPErr_BadParam );
+ if ( strValue[0] == '-' ) temp = -temp;
+ binValue->year = temp;
+ if ( strValue[pos] == 0 ) return;
+
+ ++pos;
+ temp = GatherInt ( strValue, &pos, "Invalid month in date string" ); // Extract the month.
+ if ( (strValue[pos] != 0) && (strValue[pos] != '-') ) XMP_Throw ( "Invalid date string, after month", kXMPErr_BadParam );
+ binValue->month = temp;
+ if ( strValue[pos] == 0 ) return;
+
+ ++pos;
+ temp = GatherInt ( strValue, &pos, "Invalid day in date string" ); // Extract the day.
+ if ( (strValue[pos] != 0) && (strValue[pos] != 'T') ) XMP_Throw ( "Invalid date string, after day", kXMPErr_BadParam );
+ binValue->day = temp;
+ if ( strValue[pos] == 0 ) return;
+
+ // Allow year, month, and day to all be zero; implies the date portion is missing.
+ if ( (binValue->year != 0) || (binValue->month != 0) || (binValue->day != 0) ) {
+ // Temporary fix for bug 1269463, silently fix out of range month or day.
+ // if ( (binValue->month < 1) || (binValue->month > 12) ) XMP_Throw ( "Month is out of range", kXMPErr_BadParam );
+ // if ( (binValue->day < 1) || (binValue->day > 31) ) XMP_Throw ( "Day is out of range", kXMPErr_BadParam );
+ if ( binValue->month < 1 ) binValue->month = 1;
+ if ( binValue->month > 12 ) binValue->month = 12;
+ if ( binValue->day < 1 ) binValue->day = 1;
+ if ( binValue->day > 31 ) binValue->day = 31;
+ }
+
+ }
+
+ // If we get here there is more of the string, otherwise we would have returned above.
+
+ if ( strValue[pos] == 'T' ) {
+ ++pos;
+ } else if ( ! timeOnly ) {
+ XMP_Throw ( "Invalid date string, missing 'T' after date", kXMPErr_BadParam );
+ }
+
+ binValue->hasTime = true;
+
+ temp = GatherInt ( strValue, &pos, "Invalid hour in date string" ); // Extract the hour.
+ if ( strValue[pos] != ':' ) XMP_Throw ( "Invalid date string, after hour", kXMPErr_BadParam );
+ if ( temp > 23 ) temp = 23; // *** 1269463: XMP_Throw ( "Hour is out of range", kXMPErr_BadParam );
+ binValue->hour = temp;
+ // Don't check for done, we have to work up to the time zone.
+
+ ++pos;
+ temp = GatherInt ( strValue, &pos, "Invalid minute in date string" ); // And the minute.
+ if ( (strValue[pos] != ':') && (strValue[pos] != 'Z') &&
+ (strValue[pos] != '+') && (strValue[pos] != '-') && (strValue[pos] != 0) ) XMP_Throw ( "Invalid date string, after minute", kXMPErr_BadParam );
+ if ( temp > 59 ) temp = 59; // *** 1269463: XMP_Throw ( "Minute is out of range", kXMPErr_BadParam );
+ binValue->minute = temp;
+ // Don't check for done, we have to work up to the time zone.
+
+ if ( strValue[pos] == ':' ) {
+
+ ++pos;
+ temp = GatherInt ( strValue, &pos, "Invalid whole seconds in date string" ); // Extract the whole seconds.
+ if ( (strValue[pos] != '.') && (strValue[pos] != 'Z') &&
+ (strValue[pos] != '+') && (strValue[pos] != '-') && (strValue[pos] != 0) ) {
+ XMP_Throw ( "Invalid date string, after whole seconds", kXMPErr_BadParam );
+ }
+ if ( temp > 59 ) temp = 59; // *** 1269463: XMP_Throw ( "Whole second is out of range", kXMPErr_BadParam );
+ binValue->second = temp;
+ // Don't check for done, we have to work up to the time zone.
+
+ if ( strValue[pos] == '.' ) {
+
+ ++pos;
+ size_t digits = pos; // Will be the number of digits later.
+
+ temp = GatherInt ( strValue, &pos, "Invalid fractional seconds in date string" ); // Extract the fractional seconds.
+ if ( (strValue[pos] != 'Z') && (strValue[pos] != '+') && (strValue[pos] != '-') && (strValue[pos] != 0) ) {
+ XMP_Throw ( "Invalid date string, after fractional second", kXMPErr_BadParam );
+ }
+
+ digits = pos - digits;
+ for ( ; digits > 9; --digits ) temp = temp / 10;
+ for ( ; digits < 9; ++digits ) temp = temp * 10;
+
+ if ( temp >= 1000*1000*1000 ) XMP_Throw ( "Fractional second is out of range", kXMPErr_BadParam );
+ binValue->nanoSecond = temp;
+ // Don't check for done, we have to work up to the time zone.
+
+ }
+
+ }
+
+ if ( strValue[pos] == 0 ) return;
+
+ binValue->hasTimeZone = true;
+
+ if ( strValue[pos] == 'Z' ) {
+
+ ++pos;
+
+ } else {
+
+ if ( strValue[pos] == '+' ) {
+ binValue->tzSign = kXMP_TimeEastOfUTC;
+ } else if ( strValue[pos] == '-' ) {
+ binValue->tzSign = kXMP_TimeWestOfUTC;
+ } else {
+ XMP_Throw ( "Time zone must begin with 'Z', '+', or '-'", kXMPErr_BadParam );
+ }
+
+ ++pos;
+ temp = GatherInt ( strValue, &pos, "Invalid time zone hour in date string" ); // Extract the time zone hour.
+ if ( strValue[pos] != ':' ) XMP_Throw ( "Invalid date string, after time zone hour", kXMPErr_BadParam );
+ if ( temp > 23 ) XMP_Throw ( "Time zone hour is out of range", kXMPErr_BadParam );
+ binValue->tzHour = temp;
+
+ ++pos;
+ temp = GatherInt ( strValue, &pos, "Invalid time zone minute in date string" ); // Extract the time zone minute.
+ if ( temp > 59 ) XMP_Throw ( "Time zone minute is out of range", kXMPErr_BadParam );
+ binValue->tzMinute = temp;
+
+ }
+
+ if ( strValue[pos] != 0 ) XMP_Throw ( "Invalid date string, extra chars at end", kXMPErr_BadParam );
+
+} // ConvertToDate
+
+// -------------------------------------------------------------------------------------------------
+// EncodeToBase64
+// --------------
+//
+// Encode a string of raw data bytes in base 64 according to RFC 2045. For the encoding definition
+// see section 6.8 in <http://www.ietf.org/rfc/rfc2045.txt>. Although it isn't needed for RDF, we
+// do insert a linefeed character as a newline for every 76 characters of encoded output.
+
+/* class static */ void
+XMPUtils::EncodeToBase64 ( XMP_StringPtr rawStr,
+ XMP_StringLen rawLen,
+ XMP_VarString * encodedStr )
+{
+ if ( (rawStr == 0) && (rawLen != 0) ) XMP_Throw ( "Null raw data buffer", kXMPErr_BadParam );
+ XMP_Assert ( encodedStr != 0 ); // Enforced by wrapper.
+
+ encodedStr->erase();
+ if ( rawLen == 0 ) return;
+
+ char encChunk[4];
+
+ unsigned long in, out;
+ unsigned char c1, c2, c3;
+ unsigned long merge;
+
+ const size_t outputSize = (rawLen / 3) * 4; // Approximate, might be small.
+
+ encodedStr->reserve ( outputSize );
+
+ // ----------------------------------------------------------------------------------------
+ // Each 6 bits of input produces 8 bits of output, so 3 input bytes become 4 output bytes.
+ // Process the whole chunks of 3 bytes first, then deal with any remainder. Be careful with
+ // the loop comparison, size-2 could be negative!
+
+ for ( in = 0, out = 0; (in+2) < rawLen; in += 3, out += 4 ) {
+
+ c1 = rawStr[in];
+ c2 = rawStr[in+1];
+ c3 = rawStr[in+2];
+
+ merge = (c1 << 16) + (c2 << 8) + c3;
+
+ encChunk[0] = sBase64Chars [ merge >> 18 ];
+ encChunk[1] = sBase64Chars [ (merge >> 12) & 0x3F ];
+ encChunk[2] = sBase64Chars [ (merge >> 6) & 0x3F ];
+ encChunk[3] = sBase64Chars [ merge & 0x3F ];
+
+ if ( out >= 76 ) {
+ encodedStr->append ( 1, kLF );
+ out = 0;
+ }
+ encodedStr->append ( encChunk, 4 );
+
+ }
+
+ // ------------------------------------------------------------------------------------------
+ // The output must always be a multiple of 4 bytes. If there is a 1 or 2 byte input remainder
+ // we need to create another chunk. Zero pad with bits to a 6 bit multiple, then add one or
+ // two '=' characters to pad out to 4 bytes.
+
+ switch ( rawLen - in ) {
+
+ case 0: // Done, no remainder.
+ break;
+
+ case 1: // One input byte remains.
+
+ c1 = rawStr[in];
+ merge = c1 << 16;
+
+ encChunk[0] = sBase64Chars [ merge >> 18 ];
+ encChunk[1] = sBase64Chars [ (merge >> 12) & 0x3F ];
+ encChunk[2] = encChunk[3] = '=';
+
+ if ( out >= 76 ) encodedStr->append ( 1, kLF );
+ encodedStr->append ( encChunk, 4 );
+ break;
+
+ case 2: // Two input bytes remain.
+
+ c1 = rawStr[in];
+ c2 = rawStr[in+1];
+ merge = (c1 << 16) + (c2 << 8);
+
+ encChunk[0] = sBase64Chars [ merge >> 18 ];
+ encChunk[1] = sBase64Chars [ (merge >> 12) & 0x3F ];
+ encChunk[2] = sBase64Chars [ (merge >> 6) & 0x3F ];
+ encChunk[3] = '=';
+
+ if ( out >= 76 ) encodedStr->append ( 1, kLF );
+ encodedStr->append ( encChunk, 4 );
+ break;
+
+ }
+
+} // EncodeToBase64
+
+// -------------------------------------------------------------------------------------------------
+// DecodeFromBase64
+// ----------------
+//
+// Decode a string of raw data bytes from base 64 according to RFC 2045. For the encoding definition
+// see section 6.8 in <http://www.ietf.org/rfc/rfc2045.txt>. RFC 2045 talks about ignoring all "bad"
+// input but warning about non-whitespace. For XMP use we ignore space, tab, LF, and CR. Any other
+// bad input is rejected.
+
+/* class static */ void
+XMPUtils::DecodeFromBase64 ( XMP_StringPtr encodedStr,
+ XMP_StringLen encodedLen,
+ XMP_VarString * rawStr )
+{
+ if ( (encodedStr == 0) && (encodedLen != 0) ) XMP_Throw ( "Null encoded data buffer", kXMPErr_BadParam );
+ XMP_Assert ( rawStr != 0 ); // Enforced by wrapper.
+
+ rawStr->erase();
+ if ( encodedLen == 0 ) return;
+
+ unsigned char ch, rawChunk[3];
+ unsigned long inStr, inChunk, inLimit, merge, padding;
+
+ XMP_StringLen outputSize = (encodedLen / 4) * 3; // Only a close approximation.
+
+ rawStr->reserve ( outputSize );
+
+
+ // ----------------------------------------------------------------------------------------
+ // Each 8 bits of input produces 6 bits of output, so 4 input bytes become 3 output bytes.
+ // Process all but the last 4 data bytes first, then deal with the final chunk. Whitespace
+ // in the input must be ignored. The first loop finds where the last 4 data bytes start and
+ // counts the number of padding equal signs.
+
+ padding = 0;
+ for ( inStr = 0, inLimit = encodedLen; (inStr < 4) && (inLimit > 0); ) {
+ inLimit -= 1; // ! Don't do in the loop control, the decr/test order is wrong.
+ ch = encodedStr[inLimit];
+ if ( ch == '=' ) {
+ padding += 1; // The equal sign padding is a data byte.
+ } else if ( DecodeBase64Char(ch) == 0xFF ) {
+ continue; // Ignore whitespace, don't increment inStr.
+ } else {
+ inStr += 1;
+ }
+ }
+
+ // ! Be careful to count whitespace that is immediately before the final data. Otherwise
+ // ! middle portion will absorb the final data and mess up the final chunk processing.
+
+ while ( (inLimit > 0) && (DecodeBase64Char(encodedStr[inLimit-1]) == 0xFF) ) --inLimit;
+
+ if ( inStr == 0 ) return; // Nothing but whitespace.
+ if ( padding > 2 ) XMP_Throw ( "Invalid encoded string", kXMPErr_BadParam );
+
+ // -------------------------------------------------------------------------------------------
+ // Now process all but the last chunk. The limit ensures that we have at least 4 data bytes
+ // left when entering the output loop, so the inner loop will succeed without overrunning the
+ // end of the data. At the end of the outer loop we might be past inLimit though.
+
+ inStr = 0;
+ while ( inStr < inLimit ) {
+
+ merge = 0;
+ for ( inChunk = 0; inChunk < 4; ++inStr ) { // ! Yes, increment inStr on each pass.
+ ch = DecodeBase64Char ( encodedStr [inStr] );
+ if ( ch == 0xFF ) continue; // Ignore whitespace.
+ merge = (merge << 6) + ch;
+ inChunk += 1;
+ }
+
+ rawChunk[0] = (unsigned char) (merge >> 16);
+ rawChunk[1] = (unsigned char) ((merge >> 8) & 0xFF);
+ rawChunk[2] = (unsigned char) (merge & 0xFF);
+
+ rawStr->append ( (char*)rawChunk, 3 );
+
+ }
+
+ // -------------------------------------------------------------------------------------------
+ // Process the final, possibly partial, chunk of data. The input is always a multiple 4 bytes,
+ // but the raw data can be any length. The number of padding '=' characters determines if the
+ // final chunk has 1, 2, or 3 raw data bytes.
+
+ XMP_Assert ( inStr < encodedLen );
+
+ merge = 0;
+ for ( inChunk = 0; inChunk < 4-padding; ++inStr ) { // ! Yes, increment inStr on each pass.
+ ch = DecodeBase64Char ( encodedStr[inStr] );
+ if ( ch == 0xFF ) continue; // Ignore whitespace.
+ merge = (merge << 6) + ch;
+ inChunk += 1;
+ }
+
+ if ( padding == 2 ) {
+
+ rawChunk[0] = (unsigned char) (merge >> 4);
+ rawStr->append ( (char*)rawChunk, 1 );
+
+ } else if ( padding == 1 ) {
+
+ rawChunk[0] = (unsigned char) (merge >> 10);
+ rawChunk[1] = (unsigned char) ((merge >> 2) & 0xFF);
+ rawStr->append ( (char*)rawChunk, 2 );
+
+ } else {
+
+ rawChunk[0] = (unsigned char) (merge >> 16);
+ rawChunk[1] = (unsigned char) ((merge >> 8) & 0xFF);
+ rawChunk[2] = (unsigned char) (merge & 0xFF);
+ rawStr->append ( (char*)rawChunk, 3 );
+
+ }
+
+} // DecodeFromBase64
+
+// -------------------------------------------------------------------------------------------------
+// PackageForJPEG
+// --------------
+
+/* class static */ void
+XMPUtils::PackageForJPEG ( const XMPMeta & origXMP,
+ XMP_VarString * stdStr,
+ XMP_VarString * extStr,
+ XMP_VarString * digestStr )
+{
+ XMP_Assert ( (stdStr != 0) && (extStr != 0) && (digestStr != 0) ); // ! Enforced by wrapper.
+
+ enum { kStdXMPLimit = 65000 };
+ static const char * kPacketTrailer = "<?xpacket end=\"w\"?>";
+ static size_t kTrailerLen = strlen ( kPacketTrailer );
+
+ XMP_VarString tempStr;
+ XMPMeta stdXMP, extXMP;
+ XMP_OptionBits keepItSmall = kXMP_UseCompactFormat | kXMP_OmitAllFormatting;
+
+ stdStr->erase();
+ extStr->erase();
+ digestStr->erase();
+
+ // Try to serialize everything. Note that we're making internal calls to SerializeToBuffer, so
+ // we'll be getting back the pointer and length for its internal string.
+
+ origXMP.SerializeToBuffer ( &tempStr, keepItSmall, 1, "", "", 0 );
+ #if Trace_PackageForJPEG
+ printf ( "\nXMPUtils::PackageForJPEG - Full serialize %d bytes\n", tempStr.size() );
+ #endif
+
+ if ( tempStr.size() > kStdXMPLimit ) {
+
+ // Couldn't fit everything, make a copy of the input XMP and make sure there is no xmp:Thumbnails property.
+
+ stdXMP.tree.options = origXMP.tree.options;
+ stdXMP.tree.name = origXMP.tree.name;
+ stdXMP.tree.value = origXMP.tree.value;
+ CloneOffspring ( &origXMP.tree, &stdXMP.tree );
+
+ if ( stdXMP.DoesPropertyExist ( kXMP_NS_XMP, "Thumbnails" ) ) {
+ stdXMP.DeleteProperty ( kXMP_NS_XMP, "Thumbnails" );
+ stdXMP.SerializeToBuffer ( &tempStr, keepItSmall, 1, "", "", 0 );
+ #if Trace_PackageForJPEG
+ printf ( " Delete xmp:Thumbnails, %d bytes left\n", tempStr.size() );
+ #endif
+ }
+
+ }
+
+ if ( tempStr.size() > kStdXMPLimit ) {
+
+ // Still doesn't fit, move all of the Camera Raw namespace. Add a dummy value for xmpNote:HasExtendedXMP.
+
+ stdXMP.SetProperty ( kXMP_NS_XMP_Note, "HasExtendedXMP", "123456789-123456789-123456789-12", 0 );
+
+ XMP_NodePtrPos crSchemaPos;
+ XMP_Node * crSchema = FindSchemaNode ( &stdXMP.tree, kXMP_NS_CameraRaw, kXMP_ExistingOnly, &crSchemaPos );
+
+ if ( crSchema != 0 ) {
+ crSchema->parent = &extXMP.tree;
+ extXMP.tree.children.push_back ( crSchema );
+ stdXMP.tree.children.erase ( crSchemaPos );
+ stdXMP.SerializeToBuffer ( &tempStr, keepItSmall, 1, "", "", 0 );
+ #if Trace_PackageForJPEG
+ printf ( " Move Camera Raw schema, %d bytes left\n", tempStr.size() );
+ #endif
+ }
+
+ }
+
+ if ( tempStr.size() > kStdXMPLimit ) {
+
+ // Still doesn't fit, move photoshop:History.
+
+ bool moved = MoveOneProperty ( stdXMP, &extXMP, kXMP_NS_Photoshop, "photoshop:History" );
+
+ if ( moved ) {
+ stdXMP.SerializeToBuffer ( &tempStr, keepItSmall, 1, "", "", 0 );
+ #if Trace_PackageForJPEG
+ printf ( " Move photoshop:History, %d bytes left\n", tempStr.size() );
+ #endif
+ }
+
+ }
+
+ if ( tempStr.size() > kStdXMPLimit ) {
+
+ // Still doesn't fit, move top level properties in order of estimated size. This is done by
+ // creating a multi-map that maps the serialized size to the string pair for the schema URI
+ // and top level property name. Since maps are inherently ordered, a reverse iteration of
+ // the map can be done to move the largest things first. We use a double loop to keep going
+ // until the serialization actually fits, in case the estimates are off.
+
+ PropSizeMap propSizes;
+ CreateEstimatedSizeMap ( stdXMP, &propSizes );
+
+ #if Trace_PackageForJPEG
+ if ( ! propSizes.empty() ) {
+ printf ( " Top level property map, smallest to largest:\n" );
+ PropSizeMap::iterator mapPos = propSizes.begin();
+ PropSizeMap::iterator mapEnd = propSizes.end();
+ for ( ; mapPos != mapEnd; ++mapPos ) {
+ size_t propSize = mapPos->first;
+ const char * schemaName = mapPos->second.first->c_str();
+ const char * propName = mapPos->second.second->c_str();
+ printf ( " %d bytes, %s in %s\n", propSize, propName, schemaName );
+ }
+ }
+ #endif
+
+ #if 0 // Trace_PackageForJPEG *** Xcode 2.3 on 10.4.7 has bugs in backwards iteration
+ if ( ! propSizes.empty() ) {
+ printf ( " Top level property map, largest to smallest:\n" );
+ PropSizeMap::iterator mapPos = propSizes.end();
+ PropSizeMap::iterator mapBegin = propSizes.begin();
+ for ( --mapPos; true; --mapPos ) {
+ size_t propSize = mapPos->first;
+ const char * schemaName = mapPos->second.first->c_str();
+ const char * propName = mapPos->second.second->c_str();
+ printf ( " %d bytes, %s in %s\n", propSize, propName, schemaName );
+ if ( mapPos == mapBegin ) break;
+ }
+ }
+ #endif
+
+ // Outer loop to make sure enough is actually moved.
+
+ while ( (tempStr.size() > kStdXMPLimit) && (! propSizes.empty()) ) {
+
+ // Inner loop, move what seems to be enough according to the estimates.
+
+ size_t tempLen = tempStr.size();
+ while ( (tempLen > kStdXMPLimit) && (! propSizes.empty()) ) {
+
+ size_t propSize = MoveLargestProperty ( stdXMP, &extXMP, propSizes );
+ XMP_Assert ( propSize > 0 );
+
+ if ( propSize > tempLen ) propSize = tempLen; // ! Don't go negative.
+ tempLen -= propSize;
+
+ }
+
+ // Reserialize the remaining standard XMP.
+
+ stdXMP.SerializeToBuffer ( &tempStr, keepItSmall, 1, "", "", 0 );
+
+ }
+
+ }
+
+ if ( tempStr.size() > kStdXMPLimit ) {
+ // Still doesn't fit, throw an exception and let the client decide what to do.
+ // ! This should never happen with the policy of moving any and all top level properties.
+ XMP_Throw ( "Can't reduce XMP enough for JPEG file", kXMPErr_TooLargeForJPEG );
+ }
+
+ // Set the static output strings.
+
+ if ( extXMP.tree.children.empty() ) {
+
+ // Just have the standard XMP.
+ *stdStr = tempStr;
+
+ } else {
+
+ // Have extended XMP. Serialize it, compute the digest, reset xmpNote:HasExtendedXMP, and
+ // reserialize the standard XMP.
+
+ extXMP.SerializeToBuffer ( &tempStr, (keepItSmall | kXMP_OmitPacketWrapper), 0, "", "", 0 );
+ *extStr = tempStr;
+
+ XMP_Uns8 digest [16];
+
+ {
+ context_md5_t ctx;
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, (unsigned char*)tempStr.c_str(), (unsigned int)tempStr.size() );
+ MD5Final(digest, &ctx);
+ }
+
+ digestStr->reserve ( 32 );
+ for ( size_t i = 0; i < 16; ++i ) {
+ XMP_Uns8 byte = digest[i];
+ digestStr->push_back ( kHexDigits [ byte>>4 ] );
+ digestStr->push_back ( kHexDigits [ byte&0xF ] );
+ }
+
+ stdXMP.SetProperty ( kXMP_NS_XMP_Note, "HasExtendedXMP", digestStr->c_str(), 0 );
+ stdXMP.SerializeToBuffer ( &tempStr, keepItSmall, 1, "", "", 0 );
+ *stdStr = tempStr;
+
+ }
+
+ // Adjust the standard XMP padding to be up to 2KB.
+
+ XMP_Assert ( (stdStr->size() > kTrailerLen) && (stdStr->size() <= kStdXMPLimit) );
+ const char * packetEnd = stdStr->c_str() + stdStr->size() - kTrailerLen;
+ XMP_Assert ( XMP_LitMatch ( packetEnd, kPacketTrailer ) );
+
+ size_t extraPadding = kStdXMPLimit - stdStr->size(); // ! Do this before erasing the trailer.
+ if ( extraPadding > 2047 ) extraPadding = 2047;
+ stdStr->erase ( stdStr->size() - kTrailerLen );
+ stdStr->append ( extraPadding, ' ' );
+ stdStr->append ( kPacketTrailer );
+
+} // PackageForJPEG
+
+// -------------------------------------------------------------------------------------------------
+// MergeFromJPEG
+// -------------
+//
+// Copy all of the top level properties from extendedXMP to fullXMP, replacing any duplicates.
+// Delete the xmpNote:HasExtendedXMP property from fullXMP.
+
+/* class static */ void
+XMPUtils::MergeFromJPEG ( XMPMeta * fullXMP,
+ const XMPMeta & extendedXMP )
+{
+
+ XMP_OptionBits apFlags = (kXMPTemplate_ReplaceExistingProperties | kXMPTemplate_IncludeInternalProperties);
+ XMPUtils::ApplyTemplate ( fullXMP, extendedXMP, apFlags );
+ fullXMP->DeleteProperty ( kXMP_NS_XMP_Note, "HasExtendedXMP" );
+
+} // MergeFromJPEG
+
+// -------------------------------------------------------------------------------------------------
+// CurrentDateTime
+// ---------------
+
+/* class static */ void
+XMPUtils::CurrentDateTime ( XMP_DateTime * xmpTime )
+{
+ XMP_Assert ( xmpTime != 0 ); // ! Enforced by wrapper.
+
+ ansi_tt binTime = ansi_time(0);
+ if ( binTime == -1 ) XMP_Throw ( "Failure from ANSI C time function", kXMPErr_ExternalFailure );
+ ansi_tm currTime;
+ ansi_localtime ( &binTime, &currTime );
+
+ xmpTime->year = currTime.tm_year + 1900;
+ xmpTime->month = currTime.tm_mon + 1;
+ xmpTime->day = currTime.tm_mday;
+ xmpTime->hasDate = true;
+
+ xmpTime->hour = currTime.tm_hour;
+ xmpTime->minute = currTime.tm_min;
+ xmpTime->second = currTime.tm_sec;
+ xmpTime->nanoSecond = 0;
+ xmpTime->hasTime = true;
+
+ xmpTime->tzSign = 0;
+ xmpTime->tzHour = 0;
+ xmpTime->tzMinute = 0;
+ xmpTime->hasTimeZone = false; // ! Needed for SetTimeZone.
+ XMPUtils::SetTimeZone ( xmpTime );
+
+} // CurrentDateTime
+
+// -------------------------------------------------------------------------------------------------
+// SetTimeZone
+// -----------
+//
+// Sets just the time zone part of the time. Useful for determining the local time zone or for
+// converting a "zone-less" time to a proper local time. The ANSI C time functions are smart enough
+// to do all the right stuff, as long as we call them properly!
+
+/* class static */ void
+XMPUtils::SetTimeZone ( XMP_DateTime * xmpTime )
+{
+ XMP_Assert ( xmpTime != 0 ); // ! Enforced by wrapper.
+
+ VerifyDateTimeFlags ( xmpTime );
+
+ if ( xmpTime->hasTimeZone ) {
+ XMP_Throw ( "SetTimeZone can only be used on zone-less times", kXMPErr_BadParam );
+ }
+
+ // Create ansi_tt form of the input time. Need the ansi_tm form to make the ansi_tt form.
+
+ ansi_tt ttTime;
+ ansi_tm tmLocal, tmUTC;
+
+ if ( (xmpTime->year == 0) && (xmpTime->month == 0) && (xmpTime->day == 0) ) {
+ ansi_tt now = ansi_time(0);
+ if ( now == -1 ) XMP_Throw ( "Failure from ANSI C time function", kXMPErr_ExternalFailure );
+ ansi_localtime ( &now, &tmLocal );
+ } else {
+ tmLocal.tm_year = xmpTime->year - 1900;
+ while ( tmLocal.tm_year < 70 ) tmLocal.tm_year += 4; // ! Some versions of mktime barf on years before 1970.
+ tmLocal.tm_mon = xmpTime->month - 1;
+ tmLocal.tm_mday = xmpTime->day;
+ }
+
+ tmLocal.tm_hour = xmpTime->hour;
+ tmLocal.tm_min = xmpTime->minute;
+ tmLocal.tm_sec = xmpTime->second;
+ tmLocal.tm_isdst = -1; // Don't know if daylight time is in effect.
+
+ ttTime = ansi_mktime ( &tmLocal );
+ if ( ttTime == -1 ) XMP_Throw ( "Failure from ANSI C mktime function", kXMPErr_ExternalFailure );
+
+ // Convert back to a localized ansi_tm time and get the corresponding UTC ansi_tm time.
+
+ ansi_localtime ( &ttTime, &tmLocal );
+ ansi_gmtime ( &ttTime, &tmUTC );
+
+ // Get the offset direction and amount.
+
+ ansi_tm tmx = tmLocal; // ! Note that mktime updates the ansi_tm parameter, messing up difftime!
+ ansi_tm tmy = tmUTC;
+ tmx.tm_isdst = tmy.tm_isdst = 0;
+ ansi_tt ttx = ansi_mktime ( &tmx );
+ ansi_tt tty = ansi_mktime ( &tmy );
+ double diffSecs;
+
+ if ( (ttx != -1) && (tty != -1) ) {
+ diffSecs = ansi_difftime ( ttx, tty );
+ } else {
+ #if XMP_MacBuild | XMP_iOSBuild
+ // Looks like Apple's mktime is buggy - see W1140533. But the offset is visible.
+ diffSecs = tmLocal.tm_gmtoff;
+ #else
+ // Win and UNIX don't have a visible offset. Make sure we know about the failure,
+ // then try using the current date/time as a close fallback.
+ ttTime = ansi_time(0);
+ if ( ttTime == -1 ) XMP_Throw ( "Failure from ANSI C time function", kXMPErr_ExternalFailure );
+ ansi_localtime ( &ttTime, &tmx );
+ ansi_gmtime ( &ttTime, &tmy );
+ tmx.tm_isdst = tmy.tm_isdst = 0;
+ ttx = ansi_mktime ( &tmx );
+ tty = ansi_mktime ( &tmy );
+ if ( (ttx == -1) || (tty == -1) ) XMP_Throw ( "Failure from ANSI C mktime function", kXMPErr_ExternalFailure );
+ diffSecs = ansi_difftime ( ttx, tty );
+ #endif
+ }
+
+ if ( diffSecs > 0.0 ) {
+ xmpTime->tzSign = kXMP_TimeEastOfUTC;
+ } else if ( diffSecs == 0.0 ) {
+ xmpTime->tzSign = kXMP_TimeIsUTC;
+ } else {
+ xmpTime->tzSign = kXMP_TimeWestOfUTC;
+ diffSecs = -diffSecs;
+ }
+ xmpTime->tzHour = XMP_Int32 ( diffSecs / 3600.0 );
+ xmpTime->tzMinute = XMP_Int32 ( (diffSecs / 60.0) - (xmpTime->tzHour * 60.0) );
+
+ xmpTime->hasTimeZone = xmpTime->hasTime = true;
+
+ // *** Save the tm_isdst flag in a qualifier?
+
+ XMP_Assert ( (0 <= xmpTime->tzHour) && (xmpTime->tzHour <= 23) );
+ XMP_Assert ( (0 <= xmpTime->tzMinute) && (xmpTime->tzMinute <= 59) );
+ XMP_Assert ( (-1 <= xmpTime->tzSign) && (xmpTime->tzSign <= +1) );
+ XMP_Assert ( (xmpTime->tzSign == 0) ? ((xmpTime->tzHour == 0) && (xmpTime->tzMinute == 0)) :
+ ((xmpTime->tzHour != 0) || (xmpTime->tzMinute != 0)) );
+
+} // SetTimeZone
+
+// -------------------------------------------------------------------------------------------------
+// ConvertToUTCTime
+// ----------------
+
+/* class static */ void
+XMPUtils::ConvertToUTCTime ( XMP_DateTime * time )
+{
+ XMP_Assert ( time != 0 ); // ! Enforced by wrapper.
+
+ VerifyDateTimeFlags ( time );
+
+ if ( ! time->hasTimeZone ) return; // Do nothing if there is no current time zone.
+
+ XMP_Assert ( (0 <= time->tzHour) && (time->tzHour <= 23) );
+ XMP_Assert ( (0 <= time->tzMinute) && (time->tzMinute <= 59) );
+ XMP_Assert ( (-1 <= time->tzSign) && (time->tzSign <= +1) );
+ XMP_Assert ( (time->tzSign == 0) ? ((time->tzHour == 0) && (time->tzMinute == 0)) :
+ ((time->tzHour != 0) || (time->tzMinute != 0)) );
+
+ if ( time->tzSign == kXMP_TimeEastOfUTC ) {
+ // We are before (east of) GMT, subtract the offset from the time.
+ time->hour -= time->tzHour;
+ time->minute -= time->tzMinute;
+ } else if ( time->tzSign == kXMP_TimeWestOfUTC ) {
+ // We are behind (west of) GMT, add the offset to the time.
+ time->hour += time->tzHour;
+ time->minute += time->tzMinute;
+ }
+
+ AdjustTimeOverflow ( time );
+ time->tzSign = time->tzHour = time->tzMinute = 0;
+
+} // ConvertToUTCTime
+
+// -------------------------------------------------------------------------------------------------
+// ConvertToLocalTime
+// ------------------
+
+/* class static */ void
+XMPUtils::ConvertToLocalTime ( XMP_DateTime * time )
+{
+ XMP_Assert ( time != 0 ); // ! Enforced by wrapper.
+
+ VerifyDateTimeFlags ( time );
+
+ if ( ! time->hasTimeZone ) return; // Do nothing if there is no current time zone.
+
+ XMP_Assert ( (0 <= time->tzHour) && (time->tzHour <= 23) );
+ XMP_Assert ( (0 <= time->tzMinute) && (time->tzMinute <= 59) );
+ XMP_Assert ( (-1 <= time->tzSign) && (time->tzSign <= +1) );
+ XMP_Assert ( (time->tzSign == 0) ? ((time->tzHour == 0) && (time->tzMinute == 0)) :
+ ((time->tzHour != 0) || (time->tzMinute != 0)) );
+
+ ConvertToUTCTime ( time ); // The existing time zone might not be the local one.
+ time->hasTimeZone = false; // ! Needed for SetTimeZone.
+ SetTimeZone ( time ); // Fill in the local timezone offset, then adjust the time.
+
+ if ( time->tzSign > 0 ) {
+ // We are before (east of) GMT, add the offset to the time.
+ time->hour += time->tzHour;
+ time->minute += time->tzMinute;
+ } else if ( time->tzSign < 0 ) {
+ // We are behind (west of) GMT, subtract the offset from the time.
+ time->hour -= time->tzHour;
+ time->minute -= time->tzMinute;
+ }
+
+ AdjustTimeOverflow ( time );
+
+} // ConvertToLocalTime
+
+// -------------------------------------------------------------------------------------------------
+// CompareDateTime
+// ---------------
+
+/* class static */ int
+XMPUtils::CompareDateTime ( const XMP_DateTime & _in_left,
+ const XMP_DateTime & _in_right )
+{
+ int result = 0;
+
+ XMP_DateTime left = _in_left;
+ XMP_DateTime right = _in_right;
+
+ VerifyDateTimeFlags ( &left );
+ VerifyDateTimeFlags ( &right );
+
+ // Can't compare if one has a date and the other does not.
+ if ( left.hasDate != right.hasDate ) return 0; // Throw?
+
+ if ( left.hasTimeZone & right.hasTimeZone ) {
+ // If both times have zones then convert them to UTC, otherwise assume the same zone.
+ ConvertToUTCTime ( &left );
+ ConvertToUTCTime ( &right );
+ }
+
+ if ( left.hasDate ) {
+
+ XMP_Assert ( right.hasDate );
+
+ if ( left.year < right.year ) {
+ result = -1;
+ } else if ( left.year > right.year ) {
+ result = +1;
+ } else if ( left.month < right.month ) {
+ result = -1;
+ } else if ( left.month > right.month ) {
+ result = +1;
+ } else if ( left.day < right.day ) {
+ result = -1;
+ } else if ( left.day > right.day ) {
+ result = +1;
+ }
+
+ if ( result != 0 ) return result;
+
+ }
+
+ if ( left.hasTime & right.hasTime ) {
+
+ // Ignore the time parts if either value is date-only.
+
+ if ( left.hour < right.hour ) {
+ result = -1;
+ } else if ( left.hour > right.hour ) {
+ result = +1;
+ } else if ( left.minute < right.minute ) {
+ result = -1;
+ } else if ( left.minute > right.minute ) {
+ result = +1;
+ } else if ( left.second < right.second ) {
+ result = -1;
+ } else if ( left.second > right.second ) {
+ result = +1;
+ } else if ( left.nanoSecond < right.nanoSecond ) {
+ result = -1;
+ } else if ( left.nanoSecond > right.nanoSecond ) {
+ result = +1;
+ } else {
+ result = 0;
+ }
+
+ }
+
+ return result;
+
+} // CompareDateTime
+
+// =================================================================================================
+
+std::string& XMPUtils::Trim( std::string& string )
+{
+ size_t pos = string.find_last_not_of( *WhiteSpaceStrPtr );
+
+ if ( pos != std::string::npos ) {
+ string.erase( pos + 1 );
+ pos = string.find_first_not_of( *WhiteSpaceStrPtr );
+ if(pos != std::string::npos) string.erase(0, pos);
+ } else {
+ string.erase( string.begin(), string.end() );
+ }
+ return string;
+}
+
+std::string * XMPUtils::WhiteSpaceStrPtr = NULL;
+
+// =================================================================================================
diff --git a/gpr/source/lib/xmp_core/XMPUtils.hpp b/gpr/source/lib/xmp_core/XMPUtils.hpp
new file mode 100644
index 0000000..1c99041
--- /dev/null
+++ b/gpr/source/lib/xmp_core/XMPUtils.hpp
@@ -0,0 +1,198 @@
+#ifndef __XMPUtils_hpp__
+#define __XMPUtils_hpp__
+
+// =================================================================================================
+// Copyright 2003 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.
+// =================================================================================================
+
+#include "public/include/XMP_Environment.h"
+#include "public/include/XMP_Const.h"
+
+#include "XMPMeta.hpp"
+#include "XMPCore_Impl.hpp"
+#include "public/include/client-glue/WXMPUtils.hpp"
+
+// -------------------------------------------------------------------------------------------------
+
+class XMPUtils {
+public:
+
+ static bool
+ Initialize(); // ! For internal use only!
+
+ static void
+ Terminate() RELEASE_NO_THROW; // ! For internal use only!
+
+ // ---------------------------------------------------------------------------------------------
+
+ static void
+ ComposeArrayItemPath ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex,
+ XMP_VarString * fullPath );
+
+ static void
+ ComposeStructFieldPath ( XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ XMP_VarString * fullPath );
+
+ static void
+ ComposeQualifierPath ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName,
+ XMP_VarString * fullPath );
+
+ static void
+ ComposeLangSelector ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr langName,
+ XMP_VarString * fullPath );
+
+ static void
+ ComposeFieldSelector ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ XMP_StringPtr fieldValue,
+ XMP_VarString * fullPath );
+
+ // ---------------------------------------------------------------------------------------------
+
+ static void
+ ConvertFromBool ( bool binValue,
+ XMP_VarString * strValue );
+
+ static void
+ ConvertFromInt ( XMP_Int32 binValue,
+ XMP_StringPtr format,
+ XMP_VarString * strValue );
+
+ static void
+ ConvertFromInt64 ( XMP_Int64 binValue,
+ XMP_StringPtr format,
+ XMP_VarString * strValue );
+
+ static void
+ ConvertFromFloat ( double binValue,
+ XMP_StringPtr format,
+ XMP_VarString * strValue );
+
+ static void
+ ConvertFromDate ( const XMP_DateTime & binValue,
+ XMP_VarString * strValue );
+
+ // ---------------------------------------------------------------------------------------------
+
+ static bool
+ ConvertToBool ( XMP_StringPtr strValue );
+
+ static XMP_Int32
+ ConvertToInt ( XMP_StringPtr strValue );
+
+ static XMP_Int64
+ ConvertToInt64 ( XMP_StringPtr strValue );
+
+ static double
+ ConvertToFloat ( XMP_StringPtr strValue );
+
+ static void
+ ConvertToDate ( XMP_StringPtr strValue,
+ XMP_DateTime * binValue );
+
+ // ---------------------------------------------------------------------------------------------
+
+ static void
+ CurrentDateTime ( XMP_DateTime * time );
+
+ static void
+ SetTimeZone ( XMP_DateTime * time );
+
+ static void
+ ConvertToUTCTime ( XMP_DateTime * time );
+
+ static void
+ ConvertToLocalTime ( XMP_DateTime * time );
+
+ static int
+ CompareDateTime ( const XMP_DateTime & left,
+ const XMP_DateTime & right );
+ // ---------------------------------------------------------------------------------------------
+
+ static void
+ EncodeToBase64 ( XMP_StringPtr rawStr,
+ XMP_StringLen rawLen,
+ XMP_VarString * encodedStr );
+
+ static void
+ DecodeFromBase64 ( XMP_StringPtr encodedStr,
+ XMP_StringLen encodedLen,
+ XMP_VarString * rawStr );
+
+ // ---------------------------------------------------------------------------------------------
+
+ static void
+ PackageForJPEG ( const XMPMeta & xmpObj,
+ XMP_VarString * stdStr,
+ XMP_VarString * extStr,
+ XMP_VarString * digestStr );
+
+ static void
+ MergeFromJPEG ( XMPMeta * fullXMP,
+ const XMPMeta & extendedXMP );
+
+ // ---------------------------------------------------------------------------------------------
+
+ static void
+ CatenateArrayItems ( const XMPMeta & xmpObj,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr separator,
+ XMP_StringPtr quotes,
+ XMP_OptionBits options,
+ XMP_VarString * catedStr );
+
+ static void
+ SeparateArrayItems ( XMPMeta * xmpObj,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_OptionBits options,
+ XMP_StringPtr catedStr );
+
+ static void
+ ApplyTemplate ( XMPMeta * workingXMP,
+ const XMPMeta & templateXMP,
+ XMP_OptionBits actions );
+
+ static void
+ RemoveProperties ( XMPMeta * xmpObj,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_OptionBits options );
+
+ static void
+ DuplicateSubtree ( const XMPMeta & source,
+ XMPMeta * dest,
+ XMP_StringPtr sourceNS,
+ XMP_StringPtr sourceRoot,
+ XMP_StringPtr destNS,
+ XMP_StringPtr destRoot,
+ XMP_OptionBits options );
+
+ // ---------------------------------------------------------------------------------------------
+
+ static std::string& Trim(std::string& string);
+
+ static std::string * WhiteSpaceStrPtr;
+
+}; // XMPUtils
+
+// =================================================================================================
+
+#endif // __XMPUtils_hpp__
diff --git a/gpr/source/lib/xmp_core/XMP_BuildInfo.h b/gpr/source/lib/xmp_core/XMP_BuildInfo.h
new file mode 100644
index 0000000..35fe00e
--- /dev/null
+++ b/gpr/source/lib/xmp_core/XMP_BuildInfo.h
@@ -0,0 +1,17 @@
+#ifndef __XMP_BuildInfo_h__
+#define __XMP_BuildInfo_h__ 1
+
+/*
+// =================================================================================================
+// Copyright 2002 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.
+// =================================================================================================
+*/
+
+#define kXMP_Copyright Copyright (c) 2013
+#define kXMP_CopyrightStr "Copyright (c) 2013"
+
+#endif /* __XMP_BuildInfo_h__ */
diff --git a/gpr/source/lib/xmp_core/XMP_LibUtils.cpp b/gpr/source/lib/xmp_core/XMP_LibUtils.cpp
new file mode 100644
index 0000000..507294e
--- /dev/null
+++ b/gpr/source/lib/xmp_core/XMP_LibUtils.cpp
@@ -0,0 +1,705 @@
+// =================================================================================================
+// Copyright 2009 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.
+// =================================================================================================
+
+#include "public/include/XMP_Environment.h"
+
+#include "XMP_LibUtils.hpp"
+
+#include "UnicodeInlines.incl_cpp"
+
+#include <cstdio>
+#include <cstring>
+
+// =================================================================================================
+
+#ifndef TraceThreadLocks
+ #define TraceThreadLocks 0
+#endif
+
+// -------------------------------------------------------------------------------------------------
+
+extern "C" bool Initialize_LibUtils()
+{
+ return true;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+extern "C" void Terminate_LibUtils(){
+ // Nothing to do.
+}
+
+// =================================================================================================
+// Error notifications
+// =================================================================================================
+
+bool GenericErrorCallback::CheckLimitAndSeverity ( XMP_ErrorSeverity severity ) const
+{
+
+ if ( this->limit == 0 ) return true; // Always notify if the limit is zero.
+ if ( severity < this->topSeverity ) return false; // Don't notify, don't count.
+
+ if ( severity > this->topSeverity ) {
+ this->topSeverity = severity;
+ this->notifications = 0;
+ }
+
+ this->notifications += 1;
+ return (this->notifications <= this->limit);
+
+} // GenericErrorCallback::CheckLimitAndSeverity
+
+// =================================================================================================
+
+void GenericErrorCallback::NotifyClient ( XMP_ErrorSeverity severity, XMP_Error & error, XMP_StringPtr filePath /*= 0 */ ) const
+{
+ bool notifyClient = CanNotify() && !error.IsNotified();
+ bool returnAndRecover (severity == kXMPErrSev_Recoverable);
+
+ if ( notifyClient ) {
+ error.SetNotified();
+ notifyClient = CheckLimitAndSeverity ( severity );
+ if ( notifyClient ) {
+ returnAndRecover &= ClientCallbackWrapper( filePath, severity, error.GetID(), error.GetErrMsg() );
+ }
+ }
+
+ if ( ! returnAndRecover ) XMP_Error_Throw ( error );
+
+}
+
+// =================================================================================================
+// Thread synchronization locks
+// =================================================================================================
+
+XMP_ReadWriteLock::XMP_ReadWriteLock() : beingWritten(false)
+{
+ #if XMP_DebugBuild && HaveAtomicIncrDecr
+ this->lockCount = 0;
+ // Atomic counter must be 32 or 64 bits and naturally aligned.
+ size_t counterSize = sizeof ( XMP_AtomicCounter );
+ size_t counterOffset = XMP_OffsetOf ( XMP_ReadWriteLock, lockCount );
+ XMP_Assert ( (counterSize == 4) || (counterSize == 8) ); // Counter must be 32 or 64 bits.
+ XMP_Assert ( (counterOffset & (counterSize-1)) == 0 ); // Counter must be naturally aligned.
+ #endif
+ XMP_BasicRWLock_Initialize ( this->lock );
+ #if TraceThreadLocks
+ fprintf ( stderr, "Created lock %.8X\n", this );
+ #endif
+}
+
+// ---------------------------------------------------------------------------------------------
+
+XMP_ReadWriteLock::~XMP_ReadWriteLock()
+{
+ #if TraceThreadLocks
+ fprintf ( stderr, "Deleting lock %.8X\n", this );
+ #endif
+ #if XMP_DebugBuild && HaveAtomicIncrDecr
+ XMP_Assert ( this->lockCount == 0 );
+ #endif
+ XMP_BasicRWLock_Terminate ( this->lock );
+}
+
+// ---------------------------------------------------------------------------------------------
+
+void XMP_ReadWriteLock::Acquire ( bool forWriting )
+{
+ #if TraceThreadLocks
+ fprintf ( stderr, "Acquiring lock %.8X for %s, count %d%s\n",
+ this, (forWriting ? "writing" : "reading"), this->lockCount, (this->beingWritten ? ", being written" : "") );
+ #endif
+
+ if ( forWriting ) {
+ XMP_BasicRWLock_AcquireForWrite ( this->lock );
+ #if XMP_DebugBuild && HaveAtomicIncrDecr
+ XMP_Assert ( this->lockCount == 0 );
+ #endif
+ } else {
+ XMP_BasicRWLock_AcquireForRead ( this->lock );
+ XMP_Assert ( ! this->beingWritten );
+ }
+ #if XMP_DebugBuild && HaveAtomicIncrDecr
+ XMP_AtomicIncrement ( this->lockCount );
+ #endif
+ this->beingWritten = forWriting;
+
+ #if TraceThreadLocks
+ fprintf ( stderr, "Acquired lock %.8X for %s, count %d%s\n",
+ this, (forWriting ? "writing" : "reading"), this->lockCount, (this->beingWritten ? ", being written" : "") );
+ #endif
+}
+
+// ---------------------------------------------------------------------------------------------
+
+void XMP_ReadWriteLock::Release()
+{
+ #if TraceThreadLocks
+ fprintf ( stderr, "Releasing lock %.8X, count %d%s\n", this, this->lockCount, (this->beingWritten ? ", being written" : "") );
+ #endif
+
+ #if XMP_DebugBuild && HaveAtomicIncrDecr
+ XMP_Assert ( this->lockCount > 0 );
+ XMP_AtomicDecrement ( this->lockCount ); // ! Do these before unlocking, that might release a waiting thread.
+ #endif
+ bool forWriting = this->beingWritten;
+ this->beingWritten = false;
+
+ if ( forWriting ) {
+ XMP_BasicRWLock_ReleaseFromWrite ( this->lock );
+ } else {
+ XMP_BasicRWLock_ReleaseFromRead ( this->lock );
+ }
+
+ #if TraceThreadLocks
+ fprintf ( stderr, "Released lock %.8X, count %d%s\n", this, this->lockCount, (this->beingWritten ? ", being written" : "") );
+ #endif
+}
+
+// =================================================================================================
+
+#if UseHomeGrownLock
+
+ #if XMP_MacBuild | XMP_UNIXBuild | XMP_iOSBuild
+
+ // -----------------------------------------------------------------------------------------
+
+ // About pthread mutexes and conditions:
+ //
+ // The mutex protecting the condition must be locked before waiting for the condition. A
+ // thread can wait for a condition to be signaled by calling the pthread_cond_wait
+ // subroutine. The subroutine atomically unlocks the mutex and blocks the calling thread
+ // until the condition is signaled. When the call returns, the mutex is locked again.
+
+ #define InitializeBasicMutex(mutex) { int err = pthread_mutex_init ( &mutex, 0 ); XMP_Enforce ( err == 0 ); }
+ #define TerminateBasicMutex(mutex) { int err = pthread_mutex_destroy ( &mutex ); XMP_Enforce ( err == 0 ); }
+
+ #define AcquireBasicMutex(mutex) { int err = pthread_mutex_lock ( &mutex ); XMP_Enforce ( err == 0 ); }
+ #define ReleaseBasicMutex(mutex) { int err = pthread_mutex_unlock ( &mutex ); XMP_Enforce ( err == 0 ); }
+
+ #define InitializeBasicQueue(queue) { int err = pthread_cond_init ( &queue, 0 ); XMP_Enforce ( err == 0 ); }
+ #define TerminateBasicQueue(queue) { int err = pthread_cond_destroy ( &queue ); XMP_Enforce ( err == 0 ); }
+
+ #define WaitOnBasicQueue(queue,mutex) { int err = pthread_cond_wait ( &queue, &mutex ); XMP_Enforce ( err == 0 ); }
+ #define ReleaseOneBasicQueue(queue) { int err = pthread_cond_signal ( &queue ); XMP_Enforce ( err == 0 ); }
+ #define ReleaseAllBasicQueue(queue) { int err = pthread_cond_broadcast ( &queue ); XMP_Enforce ( err == 0 ); }
+
+ // -----------------------------------------------------------------------------------------
+
+ #elif XMP_WinBuild
+
+ // -----------------------------------------------------------------------------------------
+
+ #define InitializeBasicMutex(mutex) { InitializeCriticalSection ( &mutex ); }
+ #define TerminateBasicMutex(mutex) { DeleteCriticalSection ( &mutex ); }
+
+ #define AcquireBasicMutex(mutex) { EnterCriticalSection ( &mutex ); }
+ #define ReleaseBasicMutex(mutex) { LeaveCriticalSection ( &mutex ); }
+
+ #if ! BuildLocksForWinXP
+
+ // About Win32 condition variables (not on XP):
+ //
+ // Condition variables enable threads to atomically release a lock and enter the
+ // sleeping state. They can be used with critical sections or slim reader/writer (SRW)
+ // locks. Condition variables support operations that "wake one" or "wake all" waiting
+ // threads. After a thread is woken, it re-acquires the lock it released when the thread
+ // entered the sleeping state.
+
+ #define InitializeBasicQueue(queue) { InitializeConditionVariable ( &queue ); }
+ #define TerminateBasicQueue(queue) /* Do nothing. */
+
+ #define WaitOnBasicQueue(queue,mutex) \
+ { BOOL ok = SleepConditionVariableCS ( &queue, &mutex, INFINITE /* timeout */ ); XMP_Enforce ( ok ); }
+
+ #define ReleaseOneBasicQueue(queue) { WakeConditionVariable ( &queue ); }
+ #define ReleaseAllBasicQueue(queue) { WakeAllConditionVariable ( &queue ); }
+
+ #else
+
+ // Need to create our own queue for Windows XP. This is not a general queue, it depends
+ // on the usage inside XMP_HomeGrownLock where the queueMutex guarantees that the
+ // queueing operations are done single threaded.
+
+ #define InitializeBasicQueue(queue) /* Do nothing. */
+ #define TerminateBasicQueue(queue) /* Do nothing. */
+
+ #define WaitOnBasicQueue(queue,mutex) { queue.Wait ( mutex ); }
+ #define ReleaseOneBasicQueue(queue) { queue.ReleaseOne(); }
+ #define ReleaseAllBasicQueue(queue) { queue.ReleaseAll(); }
+
+ // -------------------------------------------------------------------------------------
+
+ XMP_WinXP_HGQueue::XMP_WinXP_HGQueue() : queueEvent(0), waitCount(0), releaseAll(false)
+ {
+ this->queueEvent = CreateEvent ( NULL, FALSE, TRUE, NULL ); // Auto reset, initially clear.
+ XMP_Enforce ( this->queueEvent != 0 );
+ }
+
+ // -------------------------------------------------------------------------------------
+
+ XMP_WinXP_HGQueue::~XMP_WinXP_HGQueue()
+ {
+ CloseHandle ( this->queueEvent );
+ }
+
+ // -------------------------------------------------------------------------------------
+
+ void XMP_WinXP_HGQueue::Wait ( XMP_BasicMutex & queueMutex )
+ {
+ ++this->waitCount; // ! Does not need atomic increment, protected by queue mutex.
+ ReleaseBasicMutex ( queueMutex );
+ DWORD status = WaitForSingleObject ( this->queueEvent, INFINITE );
+ if ( status != WAIT_OBJECT_0 ) XMP_Throw ( "Failure from WaitForSingleObject", kXMPErr_ExternalFailure );
+ AcquireBasicMutex ( queueMutex );
+ --this->waitCount; // ! Does not need atomic decrement, protected by queue mutex.
+
+ if ( this->releaseAll ) {
+ if ( this->waitCount == 0 ) {
+ this->releaseAll = false;
+ } else {
+ BOOL ok = SetEvent ( this->queueEvent );
+ if ( ! ok ) XMP_Throw ( "Failure from SetEvent", kXMPErr_ExternalFailure );
+ }
+ }
+ }
+
+ // -------------------------------------------------------------------------------------
+
+ void XMP_WinXP_HGQueue::ReleaseOne()
+ {
+ XMP_Assert ( ! this->releaseAll );
+ BOOL ok = SetEvent ( this->queueEvent );
+ if ( ! ok ) XMP_Throw ( "Failure from SetEvent", kXMPErr_ExternalFailure );
+ }
+
+ // -------------------------------------------------------------------------------------
+
+ void XMP_WinXP_HGQueue::ReleaseAll()
+ {
+ this->releaseAll = true;
+ BOOL ok = SetEvent ( this->queueEvent );
+ if ( ! ok ) XMP_Throw ( "Failure from SetEvent", kXMPErr_ExternalFailure );
+ }
+
+ #endif
+
+ // -----------------------------------------------------------------------------------------
+
+ #endif
+
+ // =============================================================================================
+
+ XMP_HomeGrownLock::XMP_HomeGrownLock() : lockCount(0), readersWaiting(0), writersWaiting(0), beingWritten(false)
+ {
+ InitializeBasicMutex ( this->queueMutex );
+ InitializeBasicQueue ( this->writerQueue );
+ InitializeBasicQueue ( this->readerQueue );
+ }
+
+ // =============================================================================================
+
+ XMP_HomeGrownLock::~XMP_HomeGrownLock()
+ {
+ TerminateBasicMutex ( this->queueMutex );
+ TerminateBasicQueue ( this->writerQueue );
+ TerminateBasicQueue ( this->readerQueue );
+ }
+
+ // =============================================================================================
+
+ void XMP_HomeGrownLock::AcquireForRead()
+ {
+ XMP_AutoMutex autoMutex ( &this->queueMutex );
+
+ ++this->readersWaiting; // ! Does not need atomic increment, protected by queue mutex.
+ while ( (this->beingWritten) || (this->writersWaiting > 0) ) {
+ // Don't allow more readers if writers are waiting.
+ WaitOnBasicQueue ( this->readerQueue, this->queueMutex );
+ }
+ --this->readersWaiting; // ! Does not need atomic decrement, protected by queue mutex.
+ XMP_Assert ( ! this->beingWritten );
+
+ ++this->lockCount; // ! Does not need atomic increment, protected by queue mutex.
+ }
+
+ // =============================================================================================
+
+ void XMP_HomeGrownLock::AcquireForWrite()
+ {
+ XMP_AutoMutex autoMutex ( &this->queueMutex );
+
+ ++this->writersWaiting; // ! Does not need atomic increment, protected by queue mutex.
+ while ( this->lockCount > 0 ) {
+ WaitOnBasicQueue ( this->writerQueue, this->queueMutex );
+ }
+ --this->writersWaiting; // ! Does not need atomic decrement, protected by queue mutex.
+ XMP_Assert ( (! this->beingWritten) && (this->lockCount == 0) );
+
+ ++this->lockCount; // ! Does not need atomic increment, protected by queue mutex.
+ this->beingWritten = true;
+ }
+
+ // =============================================================================================
+
+ void XMP_HomeGrownLock::ReleaseFromRead()
+ {
+ XMP_AutoMutex autoMutex ( &this->queueMutex );
+
+ XMP_Assert ( (! this->beingWritten) && (this->lockCount > 0) );
+ --this->lockCount; // ! Does not need atomic decrement, protected by queue mutex.
+
+ if ( this->writersWaiting > 0 ) {
+ ReleaseOneBasicQueue ( this->writerQueue );
+ } else if ( this->readersWaiting > 0 ) {
+ ReleaseAllBasicQueue ( this->readerQueue );
+ }
+
+ }
+
+ // =============================================================================================
+
+ void XMP_HomeGrownLock::ReleaseFromWrite()
+ {
+ XMP_AutoMutex autoMutex ( &this->queueMutex );
+
+ XMP_Assert ( this->beingWritten && (this->lockCount == 1) );
+ --this->lockCount; // ! Does not need atomic decrement, protected by queue mutex.
+ this->beingWritten = false;
+
+ if ( this->writersWaiting > 0 ) {
+ ReleaseOneBasicQueue ( this->writerQueue );
+ } else if ( this->readersWaiting > 0 ) {
+ ReleaseAllBasicQueue ( this->readerQueue );
+ }
+ }
+
+ // =============================================================================================
+
+#endif
+
+// =================================================================================================
+// Data structure dumping utilities
+// ================================
+
+void
+DumpClearString ( const XMP_VarString & value, XMP_TextOutputProc outProc, void * refCon )
+{
+
+ char buffer [20];
+ bool prevNormal;
+ XMP_Status status = 0;
+
+ XMP_StringPtr spanStart, spanEnd;
+ XMP_StringPtr valueEnd = &value[0] + value.size();
+
+ spanStart = &value[0];
+ while ( spanStart < valueEnd ) {
+
+ // Output the next span of regular characters.
+ for ( spanEnd = spanStart; spanEnd < valueEnd; ++spanEnd ) {
+ if ( *spanEnd > 0x7F ) break;
+ if ( (*spanEnd < 0x20) && (*spanEnd != kTab) && (*spanEnd != kLF) ) break;
+ }
+ if ( spanStart != spanEnd ) status = (*outProc) ( refCon, spanStart, (XMP_StringLen)(spanEnd-spanStart) );
+ if ( status != 0 ) break;
+ spanStart = spanEnd;
+
+ // Output the next span of irregular characters.
+ prevNormal = true;
+ for ( spanEnd = spanStart; spanEnd < valueEnd; ++spanEnd ) {
+ if ( ((0x20 <= *spanEnd) && (*spanEnd <= 0x7F)) || (*spanEnd == kTab) || (*spanEnd == kLF) ) break;
+ char space = ' ';
+ if ( prevNormal ) space = '<';
+ status = (*outProc) ( refCon, &space, 1 );
+ if ( status != 0 ) break;
+ OutProcHexByte ( *spanEnd );
+ prevNormal = false;
+ }
+ if ( ! prevNormal ) {
+ status = (*outProc) ( refCon, ">", 1 );
+ if ( status != 0 ) return;
+ }
+ spanStart = spanEnd;
+
+ }
+
+} // DumpClearString
+
+// -------------------------------------------------------------------------------------------------
+
+static void
+DumpStringMap ( const XMP_StringMap & map, XMP_StringPtr label, XMP_TextOutputProc outProc, void * refCon )
+{
+ XMP_cStringMapPos currPos;
+ XMP_cStringMapPos endPos = map.end();
+
+ size_t maxLen = 0;
+ for ( currPos = map.begin(); currPos != endPos; ++currPos ) {
+ size_t currLen = currPos->first.size();
+ if ( currLen > maxLen ) maxLen = currLen;
+ }
+
+ OutProcNewline();
+ OutProcLiteral ( label );
+ OutProcNewline();
+
+ for ( currPos = map.begin(); currPos != endPos; ++currPos ) {
+ OutProcNChars ( " ", 2 );
+ DumpClearString ( currPos->first, outProc, refCon );
+ OutProcPadding ( maxLen - currPos->first.size() );
+ OutProcNChars ( " => ", 4 );
+ DumpClearString ( currPos->second, outProc, refCon );
+ OutProcNewline();
+ }
+
+} // DumpStringMap
+
+// =================================================================================================
+// Namespace Tables
+// =================================================================================================
+
+XMP_NamespaceTable::XMP_NamespaceTable ( const XMP_NamespaceTable & presets )
+{
+ XMP_AutoLock presetLock ( &presets.lock, kXMP_ReadLock );
+
+ this->uriToPrefixMap = presets.uriToPrefixMap;
+ this->prefixToURIMap = presets.prefixToURIMap;
+
+} // XMP_NamespaceTable::XMP_NamespaceTable
+
+// =================================================================================================
+
+bool XMP_NamespaceTable::Define ( XMP_StringPtr _uri, XMP_StringPtr _suggPrefix,
+ XMP_StringPtr * prefixPtr, XMP_StringLen * prefixLen )
+{
+ XMP_AutoLock tableLock ( &this->lock, kXMP_WriteLock );
+ bool prefixMatches = false;
+
+ XMP_Assert ( (_uri != 0) && (*_uri != 0) && (_suggPrefix != 0) && (*_suggPrefix != 0) );
+
+ XMP_VarString uri ( _uri );
+ XMP_VarString suggPrefix ( _suggPrefix );
+ if ( suggPrefix[suggPrefix.size()-1] != ':' ) suggPrefix += ':';
+ VerifySimpleXMLName ( _suggPrefix, _suggPrefix+suggPrefix.size()-1 ); // Exclude the colon.
+
+ XMP_StringMapPos uriPos = this->uriToPrefixMap.find ( uri );
+
+ if ( uriPos == this->uriToPrefixMap.end() ) {
+
+ // The URI is not yet registered, make sure we use a unique prefix.
+
+ XMP_VarString uniqPrefix ( suggPrefix );
+ int suffix = 0;
+ char buffer [32]; // AUDIT: Plenty of room for the "_%d_" suffix.
+
+ while ( true ) {
+ if ( this->prefixToURIMap.find ( uniqPrefix ) == this->prefixToURIMap.end() ) break;
+ ++suffix;
+ snprintf ( buffer, sizeof(buffer), "_%d_:", suffix ); // AUDIT: Using sizeof for snprintf length is safe.
+ uniqPrefix = suggPrefix;
+ uniqPrefix.erase ( uniqPrefix.size()-1 ); // ! Remove the trailing ':'.
+ uniqPrefix += buffer;
+ }
+
+ // Add the new namespace to both maps.
+
+ XMP_StringPair newNS ( uri, uniqPrefix );
+ uriPos = this->uriToPrefixMap.insert ( this->uriToPrefixMap.end(), newNS );
+
+ newNS.first.swap ( newNS.second );
+ (void) this->prefixToURIMap.insert ( this->prefixToURIMap.end(), newNS );
+
+ }
+
+ // Return the actual prefix and see if it matches the suggested prefix.
+
+ if ( prefixPtr != 0 ) *prefixPtr = uriPos->second.c_str();
+ if ( prefixLen != 0 ) *prefixLen = (XMP_StringLen)uriPos->second.size();
+
+ prefixMatches = ( uriPos->second == suggPrefix );
+ return prefixMatches;
+
+} // XMP_NamespaceTable::Define
+
+// =================================================================================================
+
+bool XMP_NamespaceTable::GetPrefix ( XMP_StringPtr _uri, XMP_StringPtr * prefixPtr, XMP_StringLen * prefixLen ) const
+{
+ XMP_AutoLock tableLock ( &this->lock, kXMP_ReadLock );
+ bool found = false;
+
+ XMP_Assert ( (_uri != 0) && (*_uri != 0) );
+
+ XMP_VarString uri ( _uri );
+ XMP_cStringMapPos uriPos = this->uriToPrefixMap.find ( uri );
+
+ if ( uriPos != this->uriToPrefixMap.end() ) {
+ if ( prefixPtr != 0 ) *prefixPtr = uriPos->second.c_str();
+ if ( prefixLen != 0 ) *prefixLen = (XMP_StringLen)uriPos->second.size();
+ found = true;
+ }
+
+ return found;
+
+} // XMP_NamespaceTable::GetPrefix
+
+// =================================================================================================
+
+bool XMP_NamespaceTable::GetURI ( XMP_StringPtr _prefix, XMP_StringPtr * uriPtr, XMP_StringLen * uriLen ) const
+{
+ XMP_AutoLock tableLock ( &this->lock, kXMP_ReadLock );
+
+ bool found = false;
+
+ XMP_Assert ( (_prefix != 0) && (*_prefix != 0) );
+
+ XMP_VarString prefix ( _prefix );
+ if ( prefix[prefix.size()-1] != ':' ) prefix += ':';
+ XMP_cStringMapPos prefixPos = this->prefixToURIMap.find ( prefix );
+
+ if ( prefixPos != this->prefixToURIMap.end() ) {
+ if ( uriPtr != 0 ) *uriPtr = prefixPos->second.c_str();
+ if ( uriLen != 0 ) *uriLen = (XMP_StringLen)prefixPos->second.size();
+ found = true;
+ }
+
+ return found;
+
+} // XMP_NamespaceTable::GetURI
+
+// =================================================================================================
+
+void XMP_NamespaceTable::Dump ( XMP_TextOutputProc outProc, void * refCon ) const
+{
+ XMP_AutoLock tableLock ( &this->lock, kXMP_ReadLock );
+
+ XMP_cStringMapPos p2uEnd = this->prefixToURIMap.end(); // ! Move up to avoid gcc complaints.
+ XMP_cStringMapPos u2pEnd = this->uriToPrefixMap.end();
+
+ DumpStringMap ( this->prefixToURIMap, "Dumping namespace prefix to URI map", outProc, refCon );
+
+ if ( this->prefixToURIMap.size() != this->uriToPrefixMap.size() ) {
+ OutProcLiteral ( "** bad namespace map sizes **" );
+ XMP_Throw ( "Fatal namespace map problem", kXMPErr_InternalFailure );
+ }
+
+ for ( XMP_cStringMapPos nsLeft = this->prefixToURIMap.begin(); nsLeft != p2uEnd; ++nsLeft ) {
+
+ XMP_cStringMapPos nsOther = this->uriToPrefixMap.find ( nsLeft->second );
+ if ( (nsOther == u2pEnd) || (nsLeft != this->prefixToURIMap.find ( nsOther->second )) ) {
+ OutProcLiteral ( " ** bad namespace URI ** " );
+ DumpClearString ( nsLeft->second, outProc, refCon );
+ break;
+ }
+
+ for ( XMP_cStringMapPos nsRight = nsLeft; nsRight != p2uEnd; ++nsRight ) {
+ if ( nsRight == nsLeft ) continue; // ! Can't start at nsLeft+1, no operator+!
+ if ( nsLeft->second == nsRight->second ) {
+ OutProcLiteral ( " ** duplicate namespace URI ** " );
+ DumpClearString ( nsLeft->second, outProc, refCon );
+ break;
+ }
+ }
+
+ }
+
+ for ( XMP_cStringMapPos nsLeft = this->uriToPrefixMap.begin(); nsLeft != u2pEnd; ++nsLeft ) {
+
+ XMP_cStringMapPos nsOther = this->prefixToURIMap.find ( nsLeft->second );
+ if ( (nsOther == p2uEnd) || (nsLeft != this->uriToPrefixMap.find ( nsOther->second )) ) {
+ OutProcLiteral ( " ** bad namespace prefix ** " );
+ DumpClearString ( nsLeft->second, outProc, refCon );
+ break;
+ }
+
+ for ( XMP_cStringMapPos nsRight = nsLeft; nsRight != u2pEnd; ++nsRight ) {
+ if ( nsRight == nsLeft ) continue; // ! Can't start at nsLeft+1, no operator+!
+ if ( nsLeft->second == nsRight->second ) {
+ OutProcLiteral ( " ** duplicate namespace prefix ** " );
+ DumpClearString ( nsLeft->second, outProc, refCon );
+ break;
+ }
+ }
+
+ }
+
+} // XMP_NamespaceTable::Dump
+
+// =================================================================================================
+static XMP_Bool matchdigit ( XMP_StringPtr text ) {
+ if ( *text >= '0' && *text <= '9' )
+ return true;
+ return false;
+}
+
+static XMP_Bool matchUpperCase ( XMP_StringPtr text ) {
+ if ( *text >= 'A' && *text <= 'Z' )
+ return true;
+ return false;
+}
+
+static XMP_Bool matchLowerCase ( XMP_StringPtr text ) {
+ if ( *text >= 'a' && *text <= 'z' )
+ return true;
+ return false;
+}
+
+/* matchhere: search for regexp at beginning of text */
+static XMP_Bool matchhere ( XMP_StringPtr regexp, XMP_StringPtr text ) {
+ if ( regexp[0] == '\0' )
+ return true;
+ if ( regexp[0] == '\\' ) {
+ if ( regexp[1] == 'd' ) {
+ if ( matchdigit(text) )
+ return matchhere ( regexp+2, text+1 );
+ else
+ return false;
+ }
+ else if ( regexp[1] == 'W' ) {
+ if ( matchUpperCase(text) )
+ return matchhere ( regexp+2, text+1 );
+ else
+ return false;
+ }
+ else if ( regexp[1] == 'w' ) {
+ if ( matchLowerCase(text) )
+ return matchhere ( regexp+2, text+1 );
+ else
+ return false;
+ }
+ }
+
+ if ( regexp[0] == '$' && regexp[1] == '\0' )
+ return *text == '\0';
+
+ if ( *text != '\0' && regexp[0] == *text )
+ return matchhere ( regexp+1, text+1 );
+ return 0;
+}
+
+/* match: search for regexp anywhere in text */
+static XMP_Bool match ( XMP_StringPtr regexp, XMP_StringPtr text ) {
+ if ( regexp[0] == '^' )
+ return matchhere ( regexp+1, text );
+ do { /* must look even if string is empty */
+ if ( matchhere ( regexp, text ) )
+ return true;
+ } while ( *text++ != '\0' );
+ return false;
+}
+
+XMP_Bool XMP_RegExp::Match ( XMP_StringPtr s )
+{
+ if ( regExpStr.size() == 0 )
+ return true;
+ if ( s == NULL )
+ return false;
+ return match ( this->regExpStr.c_str(), s );
+}
+// =================================================================================================
diff --git a/gpr/source/lib/xmp_core/XMP_LibUtils.hpp b/gpr/source/lib/xmp_core/XMP_LibUtils.hpp
new file mode 100644
index 0000000..b7a9e8f
--- /dev/null
+++ b/gpr/source/lib/xmp_core/XMP_LibUtils.hpp
@@ -0,0 +1,619 @@
+#ifndef __XMP_LibUtils_hpp__
+#define __XMP_LibUtils_hpp__ 1
+
+// =================================================================================================
+// Copyright 2009 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.
+// =================================================================================================
+
+#include "public/include/XMP_Environment.h" // ! Must be the first include.
+#include "public/include/XMP_Const.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+#if XMP_DebugBuild
+ #include <cassert>
+#endif
+
+#if XMP_WinBuild
+ #ifndef snprintf
+ #define snprintf _snprintf
+ #endif
+#endif
+
+// =================================================================================================
+// Basic types, constants
+// ======================
+
+#define kTab ((char)0x09)
+#define kLF ((char)0x0A)
+#define kCR ((char)0x0D)
+
+#if XMP_WinBuild
+ #define kDirChar '\\'
+#else
+ #define kDirChar '/'
+#endif
+
+typedef std::string XMP_VarString;
+
+#define EliminateGlobal(g) delete ( g ); g = 0
+
+extern "C" bool Initialize_LibUtils();
+extern "C" void Terminate_LibUtils();
+
+#define IgnoreParam(p) (void)p
+
+// The builtin offsetof macro sometimes violates C++ data member rules.
+#define XMP_OffsetOf(struct,field) ( (char*)(&((struct*)0x100)->field) - (char*)0x100 )
+
+// =================================================================================================
+// Support for exceptions and asserts
+// ==================================
+
+#define AnnounceThrow(msg) /* Do nothing. */
+#define AnnounceCatch(msg) /* Do nothing. */
+
+#define XMP_Throw(msg,id) { AnnounceThrow ( msg ); throw XMP_Error ( id, msg ); }
+
+#if XMP_DebugBuild
+#define XMP_Throw_Verbose(msg,e,id) \
+{ \
+ char tmpMsg[255]; \
+ snprintf(tmpMsg, sizeof(tmpMsg), #msg "( %d )", e); \
+ XMP_Throw( tmpMsg, id); \
+}
+#else
+ #define XMP_Throw_Verbose(msg,e,id) XMP_Throw(msg, id)
+#endif
+
+class GenericErrorCallback {
+public:
+ // Abstract base class for XMPCore and XMPFiles internal error notification support. Needed so
+ // that the XMLParserAdapter (used by both XMPCore and XMPFiles) can send error notifications,
+ // and so that utility parts of just XMPCore or XMPFiles can avoid dependence on XMPCore.hpp or
+ // XMPFiles.hpp if that is appropriate.
+
+ XMP_Uns32 limit;
+ mutable XMP_Uns32 notifications;
+ mutable XMP_ErrorSeverity topSeverity;
+
+ GenericErrorCallback() : notifications(0), limit(1), topSeverity(kXMPErrSev_Recoverable) {};
+ virtual ~GenericErrorCallback() {};
+
+ void Clear() { this->notifications = 0; this->limit = 1; this->topSeverity = kXMPErrSev_Recoverable; };
+
+ bool CheckLimitAndSeverity (XMP_ErrorSeverity severity ) const;
+
+ // Const so they can be used with const XMPMeta and XMPFiles objects.
+ void NotifyClient ( XMP_ErrorSeverity severity, XMP_Error & error, XMP_StringPtr filePath = 0 ) const;
+
+ virtual bool CanNotify ( ) const = 0;
+ virtual bool ClientCallbackWrapper ( XMP_StringPtr filePath, XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr messsage ) const = 0;
+
+};
+
+#define XMP_Error_Throw(error) { AnnounceThrow (error.GetErrMsg()); throw error; }
+
+
+// -------------------------------------------------------------------------------------------------
+
+#define _MakeStr(p) #p
+#define _NotifyMsg(n,c,f,l) #n " failed: " #c " in " f " at line " _MakeStr(l)
+#define _ExplicitMsg(msg,c,e) #e " " #msg ": " #c
+
+#define XMP_Validate(c,msg,e) \
+ if ( ! (c) ) { \
+ const char * validate_msg = _ExplicitMsg ( msg, c, e ); \
+ XMP_Throw ( validate_msg, e ); \
+ }
+
+// This statement is needed in XMP_Assert definition to reduce warnings from
+// static analysis tool in Visual Studio. Defined here, as platform fork not
+// possible within macro definition below
+#if XMP_WinBuild
+ #define analysis_assume(c) __analysis_assume( c );
+#else
+ #define analysis_assume(c) ((void) 0)
+#endif
+
+#if ! XMP_DebugBuild
+ #define XMP_Assert(c) ((void) 0)
+#else
+ #define XMP_Assert(c) assert ( c )
+#endif
+
+ #define XMP_Enforce(c) \
+ if ( ! (c) ) { \
+ const char * assert_msg = _NotifyMsg ( XMP_Enforce, (c), __FILE__, __LINE__ ); \
+ XMP_Throw ( assert_msg , kXMPErr_EnforceFailure ); \
+ }
+// =================================================================================================
+// Thread synchronization locks
+// ============================
+
+// About XMP and thread synchronization
+//
+// A variety of choices are provided for thread synchronization. Exactly one method must be chosen
+// by defining the appropriate symbol to 1.
+//
+// * UseNoLock - This choice turns the synchronization functions into no-ops. It must only be used
+// by single threaded clients, or clients providing their own control at a higher level.
+//
+// * UseGlobalLibraryLock - This choice uses a single per-library lock. The result is thread safe
+// but unfriendly behavior, no true concurrency. This should only be used as a debugging fallback.
+//
+// * UseBoostLock - This choice uses the Boost shared_mutex mechanism. It has the advantage of being
+// robust and being available on pretty much all platforms. It has the disadvantage of requiring
+// the developer to download, integrate, and build the Boost thread library.
+//
+// * UsePThreadLock - This choice uses the POSIX pthread rwlock mechanism. It has the advantage of
+// being robust and being available on any modern UNIX platform, including Mac OS X.
+//
+// * UseWinSlimLock - This choice uses the Windows slim reader/writer mechanism. It is robust but
+// only available on Vista and newer versions of Windows, it is not available on XP.
+//
+// * UseHomeGrownLock - This choice uses local code plus lower level synchronization primitives. It
+// has the advantage of being usable on all platforms, and having exposed and tunable policy. It
+// has the disadvantage of possibly being less robust than Boost or the O/S provided mechanisms.
+// The lower level synchronization primitives are pthread mutex and condition for UNIX (including
+// Mac OS X). For Windows there is a choice of critical section and condition variable for Vista
+// and newer; or critical section, event, and semaphore for XP and newer.
+
+#define UseNoLock 1
+
+// -------------------------------------------------------------------------------------------------
+// A basic exclusive access mutex and atomic increment/decrement operations.
+
+#if XMP_WinBuild
+
+ #include <Windows.h>
+
+ #define HaveAtomicIncrDecr 1
+ typedef LONG XMP_AtomicCounter;
+
+ #define XMP_AtomicIncrement(x) InterlockedIncrement ( &(x) )
+ #define XMP_AtomicDecrement(x) InterlockedDecrement ( &(x) )
+
+ typedef CRITICAL_SECTION XMP_BasicMutex;
+
+ #define InitializeBasicMutex(mutex) { InitializeCriticalSection ( &mutex ); }
+ #define TerminateBasicMutex(mutex) { DeleteCriticalSection ( &mutex ); }
+ #define AcquireBasicMutex(mutex) { EnterCriticalSection ( &mutex ); }
+ #define ReleaseBasicMutex(mutex) { LeaveCriticalSection ( &mutex ); }
+
+#elif XMP_MacBuild | XMP_iOSBuild
+
+ #include <pthread.h>
+ #include <libkern/OSAtomic.h>
+
+ #define HaveAtomicIncrDecr 1
+ typedef int32_t XMP_AtomicCounter;
+
+ #define XMP_AtomicIncrement(x) OSAtomicIncrement32 ( &(x) )
+ #define XMP_AtomicDecrement(x) OSAtomicDecrement32 ( &(x) )
+
+ typedef pthread_mutex_t XMP_BasicMutex;
+
+ #define InitializeBasicMutex(mutex) { int err = pthread_mutex_init ( &mutex, 0 ); XMP_Enforce ( err == 0 ); }
+ #define TerminateBasicMutex(mutex) { int err = pthread_mutex_destroy ( &mutex ); XMP_Enforce ( err == 0 ); }
+ #define AcquireBasicMutex(mutex) { int err = pthread_mutex_lock ( &mutex ); XMP_Enforce ( err == 0 ); }
+ #define ReleaseBasicMutex(mutex) { int err = pthread_mutex_unlock ( &mutex ); XMP_Enforce ( err == 0 ); }
+
+#elif XMP_UNIXBuild
+
+ #include <pthread.h>
+
+ // Atomic increment/decrement intrinsics should be in gcc 4.1, but Solaris seems to lack them.
+ #ifndef HaveAtomicIncrDecr
+ #define HaveAtomicIncrDecr 1
+ #endif
+ #if HaveAtomicIncrDecr
+ typedef XMP_Uns32 XMP_AtomicCounter;
+ #define XMP_AtomicIncrement(x) __sync_add_and_fetch ( &(x), 1 )
+ #define XMP_AtomicDecrement(x) __sync_sub_and_fetch ( &(x), 1 )
+ #endif
+
+ typedef pthread_mutex_t XMP_BasicMutex;
+
+ #define InitializeBasicMutex(mutex) { int err = pthread_mutex_init ( &mutex, 0 ); XMP_Enforce ( err == 0 ); }
+ #define TerminateBasicMutex(mutex) { int err = pthread_mutex_destroy ( &mutex ); XMP_Enforce ( err == 0 ); }
+ #define AcquireBasicMutex(mutex) { int err = pthread_mutex_lock ( &mutex ); XMP_Enforce ( err == 0 ); }
+ #define ReleaseBasicMutex(mutex) { int err = pthread_mutex_unlock ( &mutex ); XMP_Enforce ( err == 0 ); }
+
+#elif XMP_BanzaiBuild
+
+ #include <rtos_mutex.h>
+
+ // Atomic increment/decrement intrinsics should be in gcc 4.1, but Solaris seems to lack them.
+ #ifndef HaveAtomicIncrDecr
+ #define HaveAtomicIncrDecr 1
+ #endif
+ #if HaveAtomicIncrDecr
+ typedef XMP_Uns32 XMP_AtomicCounter;
+ #define XMP_AtomicIncrement(x) __sync_add_and_fetch ( &(x), 1 )
+ #define XMP_AtomicDecrement(x) __sync_sub_and_fetch ( &(x), 1 )
+ #endif
+
+ typedef rtos_mutex_t XMP_BasicMutex;
+
+ #define InitializeBasicMutex(mutex) { int err = rtos_mutex_create ( &mutex, "mutex"); XMP_Enforce ( err == 0 ); }
+ #define TerminateBasicMutex(mutex) { int err = rtos_mutex_delete ( &mutex ); XMP_Enforce ( err == 0 ); }
+ #define AcquireBasicMutex(mutex) { int err = rtos_mutex_acquire ( &mutex, RTOS_WAIT_FOREVER ); XMP_Enforce ( err == 0 ); }
+ #define ReleaseBasicMutex(mutex) { int err = rtos_mutex_release ( &mutex ); XMP_Enforce ( err == 0 ); }
+
+#endif
+
+class XMP_AutoMutex {
+public:
+ XMP_AutoMutex ( XMP_BasicMutex * _mutex ) : mutex(_mutex) { AcquireBasicMutex ( *this->mutex ); }
+ ~XMP_AutoMutex() { this->Release(); }
+ void Release() { if ( this->mutex != 0 ) ReleaseBasicMutex ( *this->mutex ); this->mutex = 0; }
+private:
+ XMP_BasicMutex * mutex;
+ XMP_AutoMutex() {}; // ! Must not be used.
+};
+
+// -------------------------------------------------------------------------------------------------
+// Details for the various locking mechanisms.
+
+#if UseNoLock
+
+ typedef void* XMP_BasicRWLock; // For single threaded clients that want maximum performance.
+
+ #define XMP_BasicRWLock_Initialize(lck) /* Do nothing. */
+ #define XMP_BasicRWLock_Terminate(lck) /* Do nothing. */
+
+ #define XMP_BasicRWLock_AcquireForRead(lck) /* Do nothing. */
+ #define XMP_BasicRWLock_AcquireForWrite(lck) /* Do nothing. */
+
+ #define XMP_BasicRWLock_ReleaseFromRead(lck) /* Do nothing. */
+ #define XMP_BasicRWLock_ReleaseFromWrite(lck) /* Do nothing. */
+
+#elif UseGlobalLibraryLock
+
+ extern XMP_BasicMutex sLibraryLock;
+
+ typedef void* XMP_BasicRWLock; // Use the old thread-unfriendly per-DLL mutex.
+
+ #define XMP_BasicRWLock_Initialize(lck) /* Do nothing. */
+ #define XMP_BasicRWLock_Terminate(lck) /* Do nothing. */
+
+ #define XMP_BasicRWLock_AcquireForRead(lck) /* Do nothing. */
+ #define XMP_BasicRWLock_AcquireForWrite(lck) /* Do nothing. */
+
+ #define XMP_BasicRWLock_ReleaseFromRead(lck) /* Do nothing. */
+ #define XMP_BasicRWLock_ReleaseFromWrite(lck) /* Do nothing. */
+
+#elif UseBoostLock
+
+ #include <boost/thread/shared_mutex.hpp>
+ typedef boost::shared_mutex XMP_BasicRWLock;
+
+ #define XMP_BasicRWLock_Initialize(lck) /* Do nothing. */
+ #define XMP_BasicRWLock_Terminate(lck) /* Do nothing. */
+
+ #define XMP_BasicRWLock_AcquireForRead(lck) lck.lock_shared()
+ #define XMP_BasicRWLock_AcquireForWrite(lck) lck.lock()
+
+ #define XMP_BasicRWLock_ReleaseFromRead(lck) lck.unlock_shared()
+ #define XMP_BasicRWLock_ReleaseFromWrite(lck) lck.unlock()
+
+#elif UsePThreadLock
+
+ #include <pthread.h>
+ typedef pthread_rwlock_t XMP_BasicRWLock;
+
+ #define XMP_BasicRWLock_Initialize(lck) \
+ { int err = pthread_rwlock_init ( &lck, 0 ); \
+ if ( err != 0 ) XMP_Throw ( "Initialize pthread rwlock failed", kXMPErr_ExternalFailure ); }
+ #define XMP_BasicRWLock_Terminate(lck) \
+ { int err = pthread_rwlock_destroy ( &lck ); XMP_Assert ( err == 0 ); }
+
+ #define XMP_BasicRWLock_AcquireForRead(lck) \
+ { int err = pthread_rwlock_rdlock ( &lck ); \
+ if ( err != 0 ) XMP_Throw ( "Acquire pthread read lock failed", kXMPErr_ExternalFailure ); }
+ #define XMP_BasicRWLock_AcquireForWrite(lck) \
+ { int err = pthread_rwlock_wrlock ( &lck ); \
+ if ( err != 0 ) XMP_Throw ( "Acquire pthread write lock failed", kXMPErr_ExternalFailure ); }
+
+ #define XMP_BasicRWLock_ReleaseFromRead(lck) \
+ { int err = pthread_rwlock_unlock ( &lck ); \
+ if ( err != 0 ) XMP_Throw ( "Release pthread read lock failed", kXMPErr_ExternalFailure ); }
+ #define XMP_BasicRWLock_ReleaseFromWrite(lck) \
+ { int err = pthread_rwlock_unlock ( &lck ); \
+ if ( err != 0 ) XMP_Throw ( "Release pthread write lock failed", kXMPErr_ExternalFailure ); }
+
+#elif UseWinSlimLock
+
+ #include <Windows.h>
+ typedef SRWLOCK XMP_BasicRWLock;
+
+ #define XMP_BasicRWLock_Initialize(lck) InitializeSRWLock ( &lck )
+ #define XMP_BasicRWLock_Terminate(lck) /* Do nothing. */
+
+ #define XMP_BasicRWLock_AcquireForRead(lck) AcquireSRWLockShared ( &lck )
+ #define XMP_BasicRWLock_AcquireForWrite(lck) AcquireSRWLockExclusive ( &lck )
+
+ #define XMP_BasicRWLock_ReleaseFromRead(lck) ReleaseSRWLockShared ( &lck )
+ #define XMP_BasicRWLock_ReleaseFromWrite(lck) ReleaseSRWLockExclusive ( &lck )
+
+#elif UseHomeGrownLock
+
+ class XMP_HomeGrownLock;
+ typedef XMP_HomeGrownLock XMP_BasicRWLock;
+
+ #define XMP_BasicRWLock_Initialize(lck) /* Do nothing. */
+ #define XMP_BasicRWLock_Terminate(lck) /* Do nothing. */
+ #define XMP_BasicRWLock_AcquireForRead(lck) lck.AcquireForRead()
+ #define XMP_BasicRWLock_AcquireForWrite(lck) lck.AcquireForWrite()
+ #define XMP_BasicRWLock_ReleaseFromRead(lck) lck.ReleaseFromRead()
+ #define XMP_BasicRWLock_ReleaseFromWrite(lck) lck.ReleaseFromWrite()
+
+ #if XMP_MacBuild | XMP_UNIXBuild | XMP_iOSBuild
+
+ #include <pthread.h>
+
+ typedef pthread_cond_t XMP_BasicQueue;
+
+ #elif XMP_WinBuild
+
+ #include <Windows.h>
+ #ifndef BuildLocksForWinXP
+ #define BuildLocksForWinXP 1
+ #endif
+
+ #if ! BuildLocksForWinXP
+ typedef CONDITION_VARIABLE XMP_BasicQueue; // ! Requires Vista or newer.
+ #else
+ class XMP_WinXP_HGQueue {
+ public:
+ XMP_WinXP_HGQueue();
+ ~XMP_WinXP_HGQueue();
+ void Wait ( XMP_BasicMutex & queueMutex );
+ void ReleaseOne();
+ void ReleaseAll();
+ private:
+ HANDLE queueEvent;
+ volatile XMP_Uns32 waitCount; // ! Does not need to be XMP_AtomicCounter.
+ volatile bool releaseAll;
+ };
+ typedef XMP_WinXP_HGQueue XMP_BasicQueue;
+ #endif
+
+ #endif
+
+ class XMP_HomeGrownLock {
+ public:
+ XMP_HomeGrownLock();
+ ~XMP_HomeGrownLock();
+ void AcquireForRead();
+ void AcquireForWrite();
+ void ReleaseFromRead();
+ void ReleaseFromWrite();
+ private:
+ XMP_BasicMutex queueMutex; // Used to protect queueing operations.
+ XMP_BasicQueue readerQueue, writerQueue;
+ volatile XMP_Uns32 lockCount, readersWaiting, writersWaiting; // ! Does not need to be XMP_AtomicCounter.
+ volatile bool beingWritten;
+ };
+
+#else
+
+ #error "No locking mechanism chosen"
+
+#endif
+
+class XMP_ReadWriteLock { // For the lock objects, use XMP_AutoLock to do the locking.
+public:
+ XMP_ReadWriteLock();
+ ~XMP_ReadWriteLock();
+ void Acquire ( bool forWriting );
+ void Release();
+private:
+ XMP_BasicRWLock lock;
+ #if XMP_DebugBuild && HaveAtomicIncrDecr
+ volatile XMP_AtomicCounter lockCount; // ! Only for debug checks, must be XMP_AtomicCounter.
+ #endif
+ volatile bool beingWritten;
+};
+
+#define kXMP_ReadLock false
+#define kXMP_WriteLock true
+
+class XMP_AutoLock {
+public:
+ XMP_AutoLock ( const XMP_ReadWriteLock * _lock, bool forWriting, bool cond = true ) : lock(0)
+ {
+ if ( cond ) {
+ // The cast below is needed because the _lock parameter might come from something
+ // like "const XMPMeta &", which would make the lock itself const. But we need to
+ // modify the lock (to acquire and release) even if the owning object is const.
+ this->lock = (XMP_ReadWriteLock*)_lock;
+ this->lock->Acquire ( forWriting );
+ }
+ }
+ ~XMP_AutoLock() { this->Release(); }
+ void Release() { if ( this->lock != 0 ) this->lock->Release(); this->lock = 0; }
+private:
+ XMP_ReadWriteLock * lock;
+ XMP_AutoLock() {}; // ! Must not be used.
+};
+
+// =================================================================================================
+// Support for wrappers
+// ====================
+
+#define AnnounceStaticEntry(proc) /* Do nothing. */
+#define AnnounceObjectEntry(proc,rwMode) /* Do nothing. */
+
+#define AnnounceExit() /* Do nothing. */
+
+// -------------------------------------------------------------------------------------------------
+
+#if UseGlobalLibraryLock
+ #define AcquireLibraryLock(lck) XMP_AutoMutex libLock ( &lck )
+#else
+ #define AcquireLibraryLock(lck) /* nothing */
+#endif
+
+#define XMP_ENTER_NoLock(Proc) \
+ AnnounceStaticEntry ( Proc ); \
+ try { \
+ wResult->errMessage = 0;
+
+#define XMP_ENTER_Static(Proc) \
+ AnnounceStaticEntry ( Proc ); \
+ AcquireLibraryLock ( sLibraryLock ); \
+ try { \
+ wResult->errMessage = 0;
+
+#define XMP_ENTER_ObjRead(XMPClass,Proc) \
+ AnnounceObjectEntry ( Proc, "reader" ); \
+ AcquireLibraryLock ( sLibraryLock ); \
+ const XMPClass & thiz = *((XMPClass*)xmpObjRef); \
+ XMP_AutoLock objLock ( &thiz.lock, kXMP_ReadLock ); \
+ try { \
+ wResult->errMessage = 0;
+
+#define XMP_ENTER_ObjWrite(XMPClass,Proc) \
+ AnnounceObjectEntry ( Proc, "writer" ); \
+ AcquireLibraryLock ( sLibraryLock ); \
+ XMPClass * thiz = (XMPClass*)xmpObjRef; \
+ XMP_AutoLock objLock ( &thiz->lock, kXMP_WriteLock ); \
+ try { \
+ wResult->errMessage = 0;
+
+#define XMP_EXIT \
+ XMP_CATCH_EXCEPTIONS \
+ AnnounceExit();
+
+#define XMP_EXIT_NoThrow \
+ } catch ( ... ) { \
+ AnnounceCatch ( "no-throw catch-all" ); \
+ /* Do nothing. */ \
+ } \
+ AnnounceExit();
+
+#define XMP_CATCH_EXCEPTIONS \
+ } catch ( XMP_Error & xmpErr ) { \
+ wResult->int32Result = xmpErr.GetID(); \
+ wResult->ptrResult = (void*)"XMP"; \
+ wResult->errMessage = xmpErr.GetErrMsg(); \
+ if ( wResult->errMessage == 0 ) wResult->errMessage = ""; \
+ AnnounceCatch ( wResult->errMessage ); \
+ } catch ( std::exception & stdErr ) { \
+ wResult->int32Result = kXMPErr_StdException; \
+ wResult->errMessage = stdErr.what(); \
+ if ( wResult->errMessage == 0 ) wResult->errMessage = ""; \
+ AnnounceCatch ( wResult->errMessage ); \
+ } catch ( ... ) { \
+ wResult->int32Result = kXMPErr_UnknownException; \
+ wResult->errMessage = "Caught unknown exception"; \
+ AnnounceCatch ( wResult->errMessage ); \
+ }
+
+#if XMP_DebugBuild
+ #define RELEASE_NO_THROW /* empty */
+#else
+ #define RELEASE_NO_THROW throw()
+#endif
+
+// =================================================================================================
+// Data structure dumping utilities
+// ================================
+
+#define IsHexDigit(ch) ( (('0' <= (ch)) && ((ch) <= '9')) || (('A' <= (ch)) && ((ch) <= 'F')) )
+#define HexDigitValue(ch) ( (((ch) - '0') < 10) ? ((ch) - '0') : ((ch) - 'A' + 10) )
+
+static const char * kTenSpaces = " ";
+#define OutProcPadding(pad) { size_t padLen = (pad); \
+ for ( ; padLen >= 10; padLen -= 10 ) OutProcNChars ( kTenSpaces, 10 ); \
+ for ( ; padLen > 0; padLen -= 1 ) OutProcNChars ( " ", 1 ); }
+
+
+#define OutProcNewline() { XMP_Status status = (*outProc) ( refCon, "\n", 1 ); if ( status != 0 ) return; }
+
+#define OutProcNChars(p,n) { XMP_Status status = (*outProc) ( refCon, (p), (n) ); if ( status != 0 ) return; }
+
+#define OutProcLiteral(lit) { XMP_Status _status = (*outProc) ( refCon, (lit), (XMP_StringLen)strlen(lit) ); if ( _status != 0 ) return; }
+
+#define OutProcString(str) { XMP_Status _status = (*outProc) ( refCon, (str).c_str(), (XMP_StringLen)(str).size() ); if ( _status != 0 ) return; }
+
+#define OutProcDecInt(num) { snprintf ( buffer, sizeof(buffer), "%ld", (long)(num) ); /* AUDIT: Using sizeof for snprintf length is safe */ \
+ buffer[sizeof(buffer) -1] = 0; /* AUDIT warning C6053: Make sure buffer is terminated */ \
+ XMP_Status _status = (*outProc) ( refCon, buffer, (XMP_StringLen)strlen(buffer) ); if ( _status != 0 ) return; }
+
+#define OutProcHexInt(num) { snprintf ( buffer, sizeof(buffer), "%lX", (long)(num) ); /* AUDIT: Using sizeof for snprintf length is safe */ \
+ buffer[sizeof(buffer) -1] = 0; /* AUDIT warning C6053: Make sure buffer is terminated */ \
+ XMP_Status _status = (*outProc) ( refCon, buffer, (XMP_StringLen)strlen(buffer) ); if ( _status != 0 ) return; }
+
+#define OutProcHexByte(num) { snprintf ( buffer, sizeof(buffer), "%.2X", (unsigned char)(num) ); /* AUDIT: Using sizeof for snprintf length is safe */ \
+ XMP_Status _status = (*outProc) ( refCon, buffer, (XMP_StringLen)strlen(buffer) ); if ( _status != 0 ) return; }
+
+static const char * kIndent = " ";
+#define OutProcIndent(lev) { for ( size_t i = 0; i < (lev); ++i ) OutProcNChars ( kIndent, 3 ); }
+
+void DumpClearString ( const XMP_VarString & value, XMP_TextOutputProc outProc, void * refCon );
+
+// =================================================================================================
+// Namespace Tables
+// ================
+typedef std::vector <XMP_VarString> XMP_StringVector;
+typedef XMP_StringVector::iterator XMP_StringVectorPos;
+typedef XMP_StringVector::const_iterator XMP_StringVectorCPos;
+
+typedef std::pair < XMP_VarString, XMP_VarString > XMP_StringPair;
+
+typedef std::map < XMP_VarString, XMP_VarString > XMP_StringMap;
+typedef XMP_StringMap::iterator XMP_StringMapPos;
+typedef XMP_StringMap::const_iterator XMP_cStringMapPos;
+
+class XMP_NamespaceTable {
+public:
+
+ XMP_NamespaceTable() {};
+ XMP_NamespaceTable ( const XMP_NamespaceTable & presets );
+ virtual ~XMP_NamespaceTable() {};
+
+ bool Define ( XMP_StringPtr uri, XMP_StringPtr suggPrefix,
+ XMP_StringPtr * prefixPtr, XMP_StringLen * prefixLen);
+
+ bool GetPrefix ( XMP_StringPtr uri, XMP_StringPtr * prefixPtr, XMP_StringLen * prefixLen ) const;
+ bool GetURI ( XMP_StringPtr prefix, XMP_StringPtr * uriPtr, XMP_StringLen * uriLen ) const;
+
+ void Dump ( XMP_TextOutputProc outProc, void * refCon ) const;
+
+private:
+
+ XMP_ReadWriteLock lock;
+ XMP_StringMap uriToPrefixMap, prefixToURIMap;
+
+};
+
+
+// Right now it supports only ^, $ and \d, in future we should use it as a wrapper over
+// regex object once mac and Linux compilers start supporting them.
+
+class XMP_RegExp {
+public:
+ XMP_RegExp ( XMP_StringPtr regExp )
+ {
+ if ( regExp )
+ regExpStr = regExp;
+ }
+
+ XMP_Bool Match ( XMP_StringPtr s );
+
+private:
+ XMP_VarString regExpStr;
+};
+
+// =================================================================================================
+
+#endif // __XMP_LibUtils_hpp__
diff --git a/gpr/source/lib/xmp_core/public/include/TXMPFiles.hpp b/gpr/source/lib/xmp_core/public/include/TXMPFiles.hpp
new file mode 100644
index 0000000..27ee413
--- /dev/null
+++ b/gpr/source/lib/xmp_core/public/include/TXMPFiles.hpp
@@ -0,0 +1,855 @@
+#ifndef __TXMPFiles_hpp__
+#define __TXMPFiles_hpp__ 1
+
+#if ( ! __XMP_hpp__ )
+ #error "Do not directly include, use XMP.hpp"
+#endif
+
+// =================================================================================================
+// ADOBE SYSTEMS INCORPORATED
+// Copyright 2002 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.
+// =================================================================================================
+
+// =================================================================================================
+/// \file TXMPFiles.hpp
+/// \brief API for access to the main (document-level) metadata in a file_.
+///
+/// The Adobe XMP Toolkit's file handling component, XMPFiles, is a front end to a set of
+/// format-specific file handlers that support file I/O for XMP. The file handlers implement smart,
+/// efficient support for those file formats for which the means to embed XMP is defined in the XMP
+/// Specification. Where possible, this support allows:
+/// \li Injection of XMP where none currently exists
+/// \li Expansion of XMP without regard to existing padding
+/// \li Reconciliation of the XMP and other legacy forms of metadata.
+///
+/// \c TXMPFiles is designed for use by clients interested in the metadata and not in the primary
+/// file content; the Adobe Bridge application is a typical example. \c TXMPFiles is not intended to
+/// be appropriate for files authored by an application; that is, those files for which the
+/// application has explicit knowledge of the file format.
+// =================================================================================================
+
+
+// =================================================================================================
+/// \class TXMPFiles TXMPFiles.hpp
+/// \brief API for access to the main (document-level) metadata in a file.
+///
+/// \c TXMPFiles is a template class that provides the API for the Adobe XMP Toolkit's XMPFiles
+/// component. This provides convenient access to the main, or document level, XMP for a file. Use
+/// it to obtain metadata from a file, which you can then manipulate with the XMP Core component
+/// (the classes \c TXMPMeta, \c TXMPUtils, and \c TXMPIterator); and to write new or changed
+/// metadata back out to a file.
+///
+/// The functions allow you to open a file, read and write the metadata, then close the file.
+/// While open, portions of the file might be maintained in RAM data structures. Memory
+/// usage can vary considerably depending onfile format and access options.
+///
+/// A file can be opened for read-only or read-write access, with typical exclusion for both
+/// modes. Errors result in the throw of an \c XMPError exception.
+///
+/// \c TXMPFiles is the template class. It must be instantiated with a string class such as
+/// \c std::string. Read the Toolkit Overview for information about the overall architecture of the XMP
+/// API, and the documentation for \c XMP.hpp for specific instantiation instructions.
+///
+/// Access these functions through the concrete class, \c SXMPFiles.
+// =================================================================================================
+
+
+#if XMP_StaticBuild // ! Client XMP_IO objects can only be used in static builds.
+ #include "XMP_IO.hpp"
+#endif
+
+
+template <class tStringObj>
+class TXMPFiles {
+
+public:
+
+ // =============================================================================================
+ /// \name Initialization and termination
+ /// @{
+ ///
+ /// A \c TXMPFiles object must be initialized before use and can be terminated when done.
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c GetVersionInfo() retrieves version information for the XMPFiles component.
+ ///
+ /// Can be called before \c #Initialize(). This function is static; make the call directly from
+ /// the concrete class (\c SXMPFiles).
+ ///
+ /// @param versionInfo [out] A buffer in which to return the version information.
+
+ static void GetVersionInfo ( XMP_VersionInfo * versionInfo );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Initializes the XMPFiles library; must be called before creating an \c SXMPFiles object.
+ ///
+ /// The main action is to activate the available smart file handlers. Must be called before
+ /// using any methods except \c GetVersionInfo().
+ ///
+ /// This function is static; make the call directly from the concrete class (\c SXMPFiles).
+ ///
+ /// @return True on success.
+
+ static bool Initialize();
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Initializes the XMPFiles library; must be called before creating an \c SXMPFiles object.
+ ///
+ /// This overload of TXMPFiles::Initialize() accepts option bits to customize the initialization
+ /// actions. At this time no option is defined.
+ ///
+ /// The main action is to activate the available smart file handlers. Must be called before
+ /// using any methods except \c GetVersionInfo().
+ ///
+ /// This function is static; make the call directly from the concrete class (\c SXMPFiles).
+ ///
+ /// @param options Option flags to control the initialization actions.
+ ///
+ /// @return True on success.
+
+ static bool Initialize ( XMP_OptionBits options );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Initializes the XMPFiles library; must be called before creating an \c SXMPFiles object.
+ ///
+ /// This overload of TXMPFiles::Initialize() accepts plugin directory and name of the plug-ins
+ /// as a comma separated list to load the file handler plug-ins. If plugins == NULL, then all
+ /// plug-ins present in the plug-in directory will be loaded.
+ ///
+ /// The main action is to activate the available smart file handlers. Must be called before
+ /// using any methods except \c GetVersionInfo().
+ ///
+ /// This function is static; make the call directly from the concrete class (\c SXMPFiles).
+ ///
+ /// @param pluginFolder Pugin directorty to load the file handler plug-ins.
+ /// @param plugins Comma sepearted list of plug-ins which should be loaded from the plug-in directory.
+ /// If plugin == NULL, then all plug-ins availbale in the plug-in directory will be loaded.
+ ///
+ /// @return True on success.
+
+ static bool Initialize ( const char* pluginFolder, const char* plugins = NULL );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Initializes the XMPFiles library; must be called before creating an \c SXMPFiles object.
+ ///
+ /// This overload of TXMPFiles::Initialize( XMP_OptionBits options ) accepts plugin directory and
+ /// name of the plug-ins as a comma separated list to load the file handler plug-ins.
+ /// If plugins == NULL, then all plug-ins present in the plug-in directory will be loaded.
+ ///
+ /// The main action is to activate the available smart file handlers. Must be called before
+ /// using any methods except \c GetVersionInfo().
+ ///
+ /// This function is static; make the call directly from the concrete class (\c SXMPFiles).
+ ///
+ /// @param options Option flags to control the initialization actions.
+ /// @param pluginFolder Pugin directorty to load the file handler plug-ins.
+ /// @param plugins Comma sepearted list of plug-ins which should be loaded from the plug-in directory.
+ /// If plugin == NULL, then all plug-ins availbale in the plug-in directory will be loaded.
+ ///
+ /// @return True on success.
+
+ static bool Initialize ( XMP_OptionBits options, const char* pluginFolder, const char* plugins = NULL );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Terminates use of the XMPFiles library.
+ ///
+ /// Optional. Deallocates global data structures created by intialization. Its main action is to
+ /// deallocate heap-allocated global storage, for the benefit of client leak checkers.
+ ///
+ /// This function is static; make the call directly from the concrete class (\c SXMPFiles).
+
+ static void Terminate();
+
+ /// @}
+
+ // =============================================================================================
+ /// \name Constructors and destructor
+ /// @{
+ ///
+ /// The default constructor initializes an object that is associated with no file. The alternate
+ /// constructors call \c OpenFile().
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Default constructor initializes an object that is associated with no file.
+
+ TXMPFiles();
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Destructor; typical virtual destructor.
+ ///
+ /// The destructor does not call \c CloseFile(); pending updates are lost when the destructor is run.
+ ///
+ /// @see \c OpenFile(), \c CloseFile()
+
+ virtual ~TXMPFiles() throw();
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Alternate constructor associates the new \c XMPFiles object with a specific file.
+ ///
+ /// Calls \c OpenFile() to open the specified file after performing a default construct.
+ ///
+ /// @param filePath The path for the file, specified as a nul-terminated UTF-8 string.
+ ///
+ /// @param format A format hint for the file, if known.
+ ///
+ /// @param openFlags Options for how the file is to be opened (for read or read/write, for
+ /// example). Use a logical OR of these bit-flag constants:
+ ///
+ /// \li \c #kXMPFiles_OpenForRead
+ /// \li \c #kXMPFiles_OpenForUpdate
+ /// \li \c #kXMPFiles_OpenOnlyXMP
+ /// \li \c #kXMPFiles_OpenStrictly
+ /// \li \c #kXMPFiles_OpenUseSmartHandler
+ /// \li \c #kXMPFiles_OpenUsePacketScanning
+ /// \li \c #kXMPFiles_OpenLimitedScanning
+ ///
+ /// @return The new \c TXMPFiles object.
+
+ TXMPFiles ( XMP_StringPtr filePath,
+ XMP_FileFormat format = kXMP_UnknownFile,
+ XMP_OptionBits openFlags = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Alternate constructor associates the new \c XMPFiles object with a specific file,
+ /// using a string object.
+ ///
+ /// Overloads the basic form of the function, allowing you to pass a string object
+ /// for the file path. It is otherwise identical; see details in the canonical form.
+
+ TXMPFiles ( const tStringObj & filePath,
+ XMP_FileFormat format = kXMP_UnknownFile,
+ XMP_OptionBits openFlags = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Copy constructor
+ ///
+ /// Increments an internal reference count but does not perform a deep copy.
+ ///
+ /// @param original The existing \c TXMPFiles object to copy.
+ ///
+ /// @return The new \c TXMPFiles object.
+
+ TXMPFiles ( const TXMPFiles<tStringObj> & original );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Assignment operator
+ ///
+ /// Increments an internal reference count but does not perform a deep copy.
+ ///
+ /// @param rhs The existing \c TXMPFiles object.
+
+ void operator= ( const TXMPFiles<tStringObj> & rhs );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Reconstructs a \c TXMPFiles object from an internal reference.
+ ///
+ /// This constructor creates a new \c TXMPFiles object that refers to the underlying reference
+ /// object of an existing \c TXMPFiles object. Use to safely pass \c SXMPFiles references across
+ /// DLL boundaries.
+ ///
+ /// @param xmpFilesObj The underlying reference object, obtained from some other XMP object
+ /// with \c TXMPFiles::GetInternalRef().
+ ///
+ /// @return The new object.
+
+ TXMPFiles ( XMPFilesRef xmpFilesObj );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief GetInternalRef() retrieves an internal reference that can be safely passed across DLL
+ /// boundaries and reconstructed.
+ ///
+ /// Use with the reconstruction constructor to safely pass \c SXMPFiles references across DLL
+ /// boundaries where the clients might have used different string types when instantiating
+ /// \c TXMPFiles.
+ ///
+ /// @return The internal reference.
+ ///
+ /// @see \c TXMPMeta::GetInternalRef() for usage.
+
+ XMPFilesRef GetInternalRef();
+
+ /// @}
+
+ // =============================================================================================
+ /// \name File handler information
+ /// @{
+ ///
+ /// Call this static function from the concrete class, \c SXMPFiles, to obtain information about
+ /// the file handlers for the XMPFiles component.
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief GetFormatInfo() reports what features are supported for a specific file format.
+ ///
+ /// The file handlers for different file formats vary considerably in what features they
+ /// support. Support depends on both the general capabilities of the format and the
+ /// implementation of the handler for that format.
+ ///
+ ///This function is static; make the call directly from the concrete class (\c SXMPFiles).
+ ///
+ /// @param format The file format whose support flags are desired.
+ ///
+ /// @param handlerFlags [out] A buffer in which to return a logical OR of option bit flags.
+ /// The following constants are defined:
+ ///
+ /// \li \c #kXMPFiles_CanInjectXMP - Can inject first-time XMP into an existing file.
+ /// \li \c #kXMPFiles_CanExpand - Can expand XMP or other metadata in an existing file.
+ /// \li \c #kXMPFiles_CanRewrite - Can copy one file to another, writing new metadata (as in SaveAs)
+ /// \li \c #kXMPFiles_CanReconcile - Supports reconciliation between XMP and other forms.
+ /// \li \c #kXMPFiles_AllowsOnlyXMP - Allows access to just the XMP, ignoring other forms.
+ /// This is only meaningful if \c #kXMPFiles_CanReconcile is set.
+ /// \li \c #kXMPFiles_ReturnsRawPacket - File handler returns raw XMP packet information and string.
+ ///
+ /// Even if \c #kXMPFiles_ReturnsRawPacket is set, the returned packet information might have an
+ /// offset of -1 to indicate an unknown offset. While all file handlers should be able to return
+ /// the raw packet, some might not know the offset of the packet within the file. This is
+ /// typical in cases where external libraries are used. These cases might not even allow return
+ /// of the raw packet.
+ ///
+ /// @return True if the format has explicit "smart" support, false if the format is handled by
+ /// the default packet scanning plus heuristics. */
+
+
+ static bool GetFormatInfo ( XMP_FileFormat format,
+ XMP_OptionBits * handlerFlags = 0 );
+
+ /// @}
+
+ // =============================================================================================
+ /// \name File operations
+ /// @{
+ ///
+ /// These functions allow you to open, close, and query files.
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c CheckFileFormat() tries to determine the format of a file.
+ ///
+ /// Tries to determine the format of a file, returning an \c #XMP_FileFormat value. Uses the
+ /// same logic as \c OpenFile() to select a smart handler.
+ ///
+ /// @param filePath The path for the file, appropriate for the local operating system. Passed as
+ /// a nul-terminated UTF-8 string. The path is the same as would be passed to \c OpenFile.
+ ///
+ /// @return The file's format if a smart handler would be selected by \c OpenFile(), otherwise
+ /// \c #kXMP_UnknownFile.
+
+ static XMP_FileFormat CheckFileFormat ( XMP_StringPtr filePath );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c CheckPackageFormat() tries to determine the format of a "package" folder.
+ ///
+ /// Tries to determine the format of a package, given the name of the top-level folder. Returns
+ /// an \c #XMP_FileFormat value. Examples of recognized packages include the video formats P2,
+ /// XDCAM, or Sony HDV. These packages contain collections of "clips", stored as multiple files
+ /// in specific subfolders.
+ ///
+ /// @param folderPath The path for the top-level folder, appropriate for the local operating
+ /// system. Passed as a nul-terminated UTF-8 string. This is not the same path you would pass to
+ /// \c OpenFile(). For example, the top-level path for a package might be ".../MyMovie", while
+ /// the path to a file you wish to open would be ".../MyMovie/SomeClip".
+ ///
+ /// @return The package's format if it can be determined, otherwise \c #kXMP_UnknownFile.
+
+ static XMP_FileFormat CheckPackageFormat ( XMP_StringPtr folderPath );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c GetFileModDate() returns the last modification date of all files that are returned
+ /// by \c GetAssociatedResources()
+ ///
+ /// Returns the most recent O/S file modification date of all associated files. In the typical case
+ /// of a single file containing embedded XMP, returned date value is the modification date of the
+ /// same file. For sidecar and folder based video packages, returned date value is the modification
+ /// date of that associated file which was updated last.
+ ///
+ /// @param filePath A path exactly as would be passed to \c OpenFile.
+ ///
+ /// @param modDate A required pointer to return the last modification date.
+ ///
+ /// @param format A format hint as would be passed to \c OpenFile.
+ ///
+ /// @param options An optional set of option flags. The only defined one is \c kXMPFiles_ForceGivenHandler,
+ /// used to shortcut the handler selection logic if the caller is certain of the format.
+ ///
+ /// @return Returns true if the file path is valid to select a smart handler, false for an
+ /// invalid path or if fallback packet scanning would be selected.
+
+ static bool GetFileModDate ( XMP_StringPtr filePath,
+ XMP_DateTime * modDate,
+ XMP_FileFormat * format = 0,
+ XMP_OptionBits options = 0 );
+
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c GetAssociatedResources() returns a list of files and folders associated to filePath.
+ ///
+ /// \c GetAssociatedResources is provided to locate all files that are associated to the given
+ /// filePath such as sidecar-based XMP or folder-based video packages.If a smart
+ /// handler can be selected (not fallback packet scanning) then a list of file/folder paths is
+ /// returned for the related files that can be safely copied/imported to a different location,
+ /// keeping intact metadata(XMP and non-XMP),content and the necessary folder structure of the
+ /// format. The necessary folder structure here is the structure that is needed to uniquely
+ /// identify a folder-based format.The filePath and format parameters are exactly as would be
+ /// used for OpenFile. In the simple embedded XMP case just one path is returned. In the simple
+ /// sidecar case one or two paths will be returned, one if there is no sidecar XMP and two if
+ /// sidecar XMP exists. For folder-based handlers paths to all associated files is returned,
+ /// including the files and folders necessary to identify the format.In general, all the returned
+ /// paths are existent.In case of folder based video formats the first associated resource in the
+ /// resourceList is the root folder.
+ ///
+ /// @param filePath A path exactly as would be passed to \c OpenFile.
+ ///
+ /// @param resourceList Address of a vector of strings to receive all associated resource paths.
+ ///
+ /// @param format A format hint as would be passed to \c OpenFile.
+ ///
+ /// @param options An optional set of option flags. The only defined one is \c kXMPFiles_ForceGivenHandler,
+ /// used to shortcut the handler selection logic if the caller is certain of the format.
+ ///
+ /// @return Returns true if the file path is valid to select a smart handler, false for an
+ /// invalid path or if fallback packet scanning would be selected. Can also return false for
+ /// unexpected errors that prevent knowledge of the file usage.
+
+ static bool GetAssociatedResources ( XMP_StringPtr filePath,
+ std::vector<tStringObj>* resourceList,
+ XMP_FileFormat format = kXMP_UnknownFile,
+ XMP_OptionBits options = 0);
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c IsMetadataWritable() returns true if metadata can be updated for the given media path.
+ ///
+ /// \c IsMetadataWritable is provided to check if metadata can be updated or written to the format.In
+ /// the case of folder-based video formats only if all the metadata files can be written to, true is
+ /// returned.In other words, false is returned for a partial-write state of metadata files in
+ /// folder-based media formats.
+ ///
+ /// @param filePath A path exactly as would be passed to \c OpenFile.
+ ///
+ /// @param writable A pointer to the result flag. Is true if the metadata can be updated in the format,
+ /// otherwise false.
+ ///
+ /// @param format A format hint as would be passed to \c OpenFile.
+ ///
+ /// @param options An optional set of option flags. The only defined one is \c kXMPFiles_ForceGivenHandler,
+ /// used to shortcut the handler selection logic if the caller is certain of the format.
+ ///
+ /// @return Returns true if the file path is valid to select a smart handler, false for an
+ /// invalid path or if fallback packet scanning would be selected.
+
+ static bool IsMetadataWritable (XMP_StringPtr filePath,
+ bool * writable,
+ XMP_FileFormat format = kXMP_UnknownFile,
+ XMP_OptionBits options = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c OpenFile() opens a file for metadata access.
+ ///
+ /// Opens a file for the requested forms of metadata access. Opening the file at a minimum
+ /// causes the raw XMP packet to be read from the file. If the file handler supports legacy
+ /// metadata reconciliation then legacy metadata is also read, unless \c #kXMPFiles_OpenOnlyXMP
+ /// is passed.
+ ///
+ /// If the file is opened for read-only access (passing \c #kXMPFiles_OpenForRead), the disk
+ /// file is closed immediately after reading the data from it; the \c XMPFiles object, however,
+ /// remains in the open state. You must call \c CloseFile() when finished using it. Other
+ /// methods, such as \c GetXMP(), can only be used between the \c OpenFile() and \c CloseFile()
+ /// calls. The \c XMPFiles destructor does not call \c CloseFile(); if you call it without
+ /// closing, any pending updates are lost.
+ ///
+ /// If the file is opened for update (passing \c #kXMPFiles_OpenForUpdate), the disk file
+ /// remains open until \c CloseFile() is called. The disk file is only updated once, when
+ /// \c CloseFile() is called, regardless of how many calls are made to \c PutXMP().
+ ///
+ /// Typically, the XMP is not parsed and legacy reconciliation is not performed until \c GetXMP()
+ /// is called, but this is not guaranteed. Specific file handlers might do earlier parsing of
+ /// the XMP. Delayed parsing and early disk file close for read-only access are optimizations
+ /// to help clients implementing file browsers, so that they can access the file briefly
+ /// and possibly display a thumbnail, then postpone more expensive XMP processing until later.
+ ///
+ /// @param filePath The path for the file, appropriate for the local operating system. Passed as
+ /// a nul-terminated UTF-8 string.
+ ///
+ /// @param format The format of the file. If the format is unknown (\c #kXMP_UnknownFile) the
+ /// format is determined from the file content. The first handler to check is guessed from the
+ /// file's extension. Passing a specific format value is generally just a hint about what file
+ /// handler to try first (instead of the one based on the extension). If
+ /// \c #kXMPFiles_OpenStrictly is set, then any format other than \c #kXMP_UnknownFile requires
+ /// that the file actually be that format; otherwise an exception is thrown.
+ ///
+ /// @param openFlags A set of option flags that describe the desired access. By default (zero)
+ /// the file is opened for read-only access and the format handler decides on the level of
+ /// reconciliation that will be performed. A logical OR of these bit-flag constants:
+ ///
+ /// \li \c #kXMPFiles_OpenForRead - Open for read-only access.
+ /// \li \c #kXMPFiles_OpenForUpdate - Open for reading and writing.
+ /// \li \c #kXMPFiles_OpenOnlyXMP - Only the XMP is wanted, no reconciliation.
+ /// \li \c #kXMPFiles_OpenStrictly - Be strict about locating XMP and reconciling with other
+ /// forms. By default, a best effort is made to locate the correct XMP and to reconcile XMP
+ /// with other forms (if reconciliation is done). This option forces stricter rules, resulting
+ /// in exceptions for errors. The definition of strictness is specific to each handler, there
+ /// might be no difference.
+ /// \li \c #kXMPFiles_OpenUseSmartHandler - Require the use of a smart handler.
+ /// \li \c #kXMPFiles_OpenUsePacketScanning - Force packet scanning, do not use a smart handler.
+ /// \li \c #kXMPFiles_OptimizeFileLayout - When updating a file, spend the effort necessary
+ /// to optimize file layout.
+ ///
+ /// @return True if the file is succesfully opened and attached to a file handler. False for
+ /// anticipated problems, such as passing \c #kXMPFiles_OpenUseSmartHandler but not having an
+ /// appropriate smart handler. Throws an exception for serious problems.
+
+ bool OpenFile ( XMP_StringPtr filePath,
+ XMP_FileFormat format = kXMP_UnknownFile,
+ XMP_OptionBits openFlags = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c OpenFile() opens a file for metadata access, using a string object
+ ///
+ /// Overloads the basic form of the function, allowing you to pass a string object for the file
+ /// path. It is otherwise identical; see details in the canonical form.
+
+ bool OpenFile ( const tStringObj & filePath,
+ XMP_FileFormat format = kXMP_UnknownFile,
+ XMP_OptionBits openFlags = 0 );
+
+ #if XMP_StaticBuild // ! Client XMP_IO objects can only be used in static builds.
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c OpenFile() opens a client-provided XMP_IO object for metadata access.
+ ///
+ /// Alternative to the basic form of the function, allowing you to pass an XMP_IO object for
+ /// client-managed I/O.
+ ///
+
+ bool OpenFile ( XMP_IO * clientIO,
+ XMP_FileFormat format = kXMP_UnknownFile,
+ XMP_OptionBits openFlags = 0 );
+ #endif
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief CloseFile() explicitly closes an opened file.
+ ///
+ /// Performs any necessary output to the file and closes it. Files that are opened for update
+ /// are written to only when closing.
+ ///
+ /// If the file is opened for read-only access (passing \c #kXMPFiles_OpenForRead), the disk
+ /// file is closed immediately after reading the data from it; the \c XMPFiles object, however,
+ /// remains in the open state. You must call \c CloseFile() when finished using it. Other
+ /// methods, such as \c GetXMP(), can only be used between the \c OpenFile() and \c CloseFile()
+ /// calls. The \c XMPFiles destructor does not call \c CloseFile(); if you call it without closing,
+ /// any pending updates are lost.
+ ///
+ /// If the file is opened for update (passing \c #kXMPFiles_OpenForUpdate), the disk file remains
+ /// open until \c CloseFile() is called. The disk file is only updated once, when \c CloseFile()
+ /// is called, regardless of how many calls are made to \c PutXMP().
+ ///
+ /// @param closeFlags Option flags for optional closing actions. This bit-flag constant is
+ /// defined:
+ ///
+ /// \li \c #kXMPFiles_UpdateSafely - Write into a temporary file then swap for crash safety.
+
+ void CloseFile ( XMP_OptionBits closeFlags = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c GetFileInfo() retrieves basic information about an opened file.
+ ///
+ /// @param filePath [out] A buffer in which to return the path passed to \c OpenFile(). Can be
+ /// null if value is not wanted.
+ ///
+ /// @param openFlags [out] A buffer in which to return the option flags passed to
+ /// \c OpenFile(). Can be null if value is not wanted.
+ ///
+ /// @param format [out] A buffer in which to return the file format. Can be null if value is not
+ /// wanted.
+ /// @param handlerFlags [out] A buffer in which to return the handler's capability flags. Can
+ /// be null if value is not wanted.
+ ///
+ /// @return True if the file object is in the open state; that is, \c OpenFile() has been called
+ /// but \c CloseFile() has not. False otherwise. Even if the file object is open, the actual
+ /// disk file might be closed in the host file-system sense; see \c OpenFile().
+
+ bool GetFileInfo ( tStringObj * filePath = 0,
+ XMP_OptionBits * openFlags = 0,
+ XMP_FileFormat * format = 0,
+ XMP_OptionBits * handlerFlags = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c SetAbortProc() registers a callback function used to check for a user-signaled abort.
+ ///
+ /// The specified procedure is called periodically to allow a user to cancel time-consuming
+ /// operations. The callback function should return true to signal an abort, which results in an
+ /// exception being thrown.
+ ///
+ /// @param abortProc The callback function.
+ ///
+ /// @param abortArg A pointer to caller-defined data to pass to the callback function.
+
+ void SetAbortProc ( XMP_AbortProc abortProc,
+ void * abortArg );
+
+ /// @}
+
+ // =============================================================================================
+ /// \name Accessing metadata
+ /// @{
+ ///
+ /// These functions allow you to retrieve XMP metadata from open files, so that you can use the
+ /// \c TXMPMeta API to manipulate it. The \c PutXMP() functions update the XMP packet in memory.
+ /// Changed XMP is not actually written out to the file until the file is closed.
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c GetXMP() retrieves the XMP metadata from an open file.
+ ///
+ /// The function reports whether XMP is present in the file; you can choose to retrieve any or
+ /// all of the parsed XMP, the raw XMP packet,or information about the raw XMP packet. The
+ /// options provided when the file was opened determine if reconciliation is done with other
+ /// forms of metadata.
+ ///
+ /// @param xmpObj [out] An XMP object in which to return the parsed XMP metadata. Can be null.
+ ///
+ /// @param xmpPacket [out] An string object in which to return the raw XMP packet as stored in
+ /// the file. Can be null. The encoding of the packet is given in the \c packetInfo. Returns an
+ /// empty string if the low level file handler does not provide the raw packet.
+ ///
+ /// @param packetInfo [out] An string object in which to return the location and form of the raw
+ /// XMP in the file. \c #XMP_PacketInfo::charForm and \c #XMP_PacketInfo::writeable reflect the
+ /// raw XMP in the file. The parsed XMP property values are always UTF-8. The writeable flag is
+ /// taken from the packet trailer; it applies only to "format ignorant" writing. The
+ /// \c #XMP_PacketInfo structure always reflects the state of the XMP in the file. The offset,
+ /// length, and character form do not change as a result of calling \c PutXMP() unless the file
+ /// is also written. Some file handlers might not return location or contents of the raw packet
+ /// string. To determine whether one does, check the \c #kXMPFiles_ReturnsRawPacket bit returned
+ /// by \c GetFormatInfo(). If the low-level file handler does not provide the raw packet
+ /// location, \c #XMP_PacketInfo::offset and \c #XMP_PacketInfo::length are both 0,
+ /// \c #XMP_PacketInfo::charForm is UTF-8, and \c #XMP_PacketInfo::writeable is false.
+ ///
+ /// @return True if the file has XMP, false otherwise.
+
+ bool GetXMP ( SXMPMeta * xmpObj = 0,
+ tStringObj * xmpPacket = 0,
+ XMP_PacketInfo * packetInfo = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c PutXMP() updates the XMP metadata in this object without writing out the file.
+ ///
+ /// This function supplies new XMP for the file. However, the disk file is not written until the
+ /// object is closed with \c CloseFile(). The options provided when the file was opened
+ /// determine if reconciliation is done with other forms of metadata.
+ ///
+ /// @param xmpObj The new metadata as an XMP object.
+
+ void PutXMP ( const SXMPMeta & xmpObj );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c PutXMP() updates the XMP metadata in this object without writing out the file,
+ /// using a string object for input.
+ ///
+ /// Overloads the basic form of the function, allowing you to pass the metadata as a string object
+ /// instead of an XMP object. It is otherwise identical; see details in the canonical form.
+ ///
+ /// @param xmpPacket The new metadata as a string object containing a complete XMP packet.
+
+ void PutXMP ( const tStringObj & xmpPacket );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c PutXMP() updates the XMP metadata in this object without writing out the file,
+ /// using a string object and optional length.
+ ///
+ /// Overloads the basic form of the function, allowing you to pass the metadata as a string object
+ /// instead of an XMP object. It is otherwise identical; see details in the canonical form.
+ ///
+ /// @param xmpPacket The new metadata as a <tt>const char *</tt> string containing an XMP packet.
+ ///
+ /// @param xmpLength Optional. The number of bytes in the string. If not supplied, the string is
+ /// assumed to be nul-terminated.
+
+ void PutXMP ( XMP_StringPtr xmpPacket,
+ XMP_StringLen xmpLength = kXMP_UseNullTermination );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c CanPutXMP() reports whether this file can be updated with a specific XMP packet.
+ ///
+ /// Use to determine if the file can probably be updated with a given set of XMP metadata. This
+ /// depends on the size of the packet, the options with which the file was opened, and the
+ /// capabilities of the handler for the file format. The function obtains the length of the
+ /// serialized packet for the provided XMP, but does not keep it or modify it, and does not
+ /// cause the file to be written when closed. This is implemented roughly as follows:
+ ///
+ /// <pre>
+ /// bool CanPutXMP ( XMP_StringPtr xmpPacket )
+ /// {
+ /// XMP_FileFormat format;
+ /// this->GetFileInfo ( 0, &format, 0 );
+ ///
+ /// XMP_OptionBits formatFlags;
+ /// GetFormatInfo ( format, &formatFlags );
+ ///
+ /// if ( (formatFlags & kXMPFiles_CanInjectXMP) && (formatFlags & kXMPFiles_CanExpand) ) return true;
+ ///
+ /// XMP_PacketInfo packetInfo;
+ /// bool hasXMP = this->GetXMP ( 0, 0, &packetInfo );
+ ///
+ /// if ( ! hasXMP ) {
+ /// if ( formatFlags & kXMPFiles_CanInjectXMP ) return true;
+ /// } else {
+ /// if ( (formatFlags & kXMPFiles_CanExpand) ||
+ /// (packetInfo.length >= strlen(xmpPacket)) ) return true;
+ /// }
+ /// return false;
+ /// }
+ /// </pre>
+ ///
+ /// @param xmpObj The proposed new metadata as an XMP object.
+
+ bool CanPutXMP ( const SXMPMeta & xmpObj );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c CanPutXMP() reports whether this file can be updated with a specific XMP packet,
+ /// passed in a string object.
+ ///
+ /// Overloads the basic form of the function, allowing you to pass the metadata as a string object
+ /// instead of an XMP object. It is otherwise identical; see details in the canonical form.
+ ///
+ /// @param xmpPacket The proposed new metadata as a string object containing an XMP packet.
+
+ bool CanPutXMP ( const tStringObj & xmpPacket );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c CanPutXMP() reports whether this file can be updated with a specific XMP packet,
+ /// passed in a string object.
+ ///
+ /// Overloads the basic form of the function, allowing you to pass the metadata as a string object
+ /// instead of an XMP object. It is otherwise identical; see details in the canonical form.
+ ///
+ /// @param xmpPacket The proposed new metadata as a <tt>const char *</tt> string containing an XMP packet.
+ ///
+ /// @param xmpLength Optional. The number of bytes in the string. If not supplied, the string
+ /// is assumed to be nul-terminated.
+
+ bool CanPutXMP ( XMP_StringPtr xmpPacket,
+ XMP_StringLen xmpLength = kXMP_UseNullTermination );
+
+ /// @}
+
+ // =============================================================================================
+ /// \name Progress notifications
+ /// @{
+ ///
+ /// These functions allow track the progress of file operations. Initially only file updates are
+ /// tracked, these all occur within calls to SXMPFiles::CloseFile. There are no plans to track
+ /// other operations at this time. Tracking support must be added to specific file handlers,
+ /// there are no guarantees about which handlers will have support. To simplify the logic only
+ /// file writes will be estimated and measured.
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c SetDefaultProgressCallback() sets a global default for progress tracking. This is
+ /// used as a default for XMPFiles (library) objects created after the default is set. This does
+ /// not affect the callback for new SXMPFiles (client) objects with an existing XMPFiles object.
+ ///
+ /// @param proc The client's callback function. Can be zero to disable notifications.
+ ///
+ /// @param context A pointer used to carry client-private context.
+ ///
+ /// @param interval The desired number of seconds between notifications. Ideally the first
+ /// notification is sent after this interval, then at each following multiple of this interval.
+ ///
+ /// @param sendStartStop A Boolean value indicating if initial and final notifications are
+ /// wanted in addition to those at the reporting intervals.
+
+ static void SetDefaultProgressCallback ( XMP_ProgressReportProc proc, void * context = 0,
+ float interval = 1.0, bool sendStartStop = false );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c SetProgressCallback() sets the progress notification callback for the associated
+ /// XMPFiles (library) object.
+ ///
+ /// @param proc The client's callback function. Can be zero to disable notifications.
+ ///
+ /// @param context A pointer used to carry client-private context.
+ ///
+ /// @param interval The desired number of seconds between notifications. Ideally the first
+ /// notification is sent after this interval, then at each following multiple of this interval.
+ ///
+ /// @param sendStartStop A Boolean value indicating if initial and final notifications are
+ /// wanted in addition to those at the reporting intervals.
+
+ void SetProgressCallback ( XMP_ProgressReportProc proc, void * context = 0,
+ float interval = 1.0, bool sendStartStop = false );
+
+ /// @}
+
+ // =============================================================================================
+ // Error notifications
+ // ===================
+
+ // ---------------------------------------------------------------------------------------------
+ /// \name Error notifications
+ /// @{
+ ///
+ /// From the beginning through version 5.5, XMP Toolkit errors result in throwing an \c XMP_Error
+ /// exception. For the most part exceptions were thrown early and thus API calls aborted as soon
+ /// as an error was detected. Starting in version 5.5, support has been added for notifications
+ /// of errors arising in calls to \c TXMPFiles functions.
+ ///
+ /// A client can register an error notification callback function for a \c TXMPFile object. This
+ /// can be done as a global default or individually to each object. The global default applies
+ /// to all objects created after it is registered. Within the object there is no difference
+ /// between the global default or explicitly registered callback. The callback function returns
+ /// a \c bool value indicating if recovery should be attempted (true) or an exception thrown
+ /// (false). If no callback is registered, a best effort at recovery and continuation will be
+ /// made with an exception thrown if recovery is not possible.
+ ///
+ /// The number of notifications delivered for a given TXMPFiles object can be limited. This is
+ /// intended to reduce chatter from multiple or cascading errors. The limit is set when the
+ /// callback function is registered. This limits the number of notifications of the highest
+ /// severity delivered or less. If a higher severity error occurs, the counting starts again.
+ /// The limit and counting can be reset at any time, see \c ResetErrorCallbackLimit.
+
+ // --------------------------------------------------------------------------------------------
+ /// @brief SetDefaultErrorCallback() registers a global default error notification callback.
+ ///
+ /// @param proc The client's callback function.
+ ///
+ /// @param context Client-provided context for the callback.
+ ///
+ /// @param limit A limit on the number of notifications to be delivered.
+
+ static void SetDefaultErrorCallback ( XMPFiles_ErrorCallbackProc proc, void* context = 0, XMP_Uns32 limit = 1 );
+
+ // --------------------------------------------------------------------------------------------
+ /// @brief SetErrorCallback() registers an error notification callback.
+ ///
+ /// @param proc The client's callback function.
+ ///
+ /// @param context Client-provided context for the callback.
+ ///
+ /// @param limit A limit on the number of notifications to be delivered.
+
+ void SetErrorCallback ( XMPFiles_ErrorCallbackProc proc, void* context = 0, XMP_Uns32 limit = 1 );
+
+ // --------------------------------------------------------------------------------------------
+ /// @brief ResetErrorCallbackLimit() resets the error notification limit and counting. It has no
+ /// effect if an error notification callback function is not registered.
+ ///
+ /// @param limit A limit on the number of notifications to be delivered.
+
+ void ResetErrorCallbackLimit ( XMP_Uns32 limit = 1 );
+
+ /// @}
+
+ // =============================================================================================
+
+private:
+
+ XMPFilesRef xmpFilesRef;
+
+ // These are used as callbacks from the library code to the client when returning values that
+ // involve heap allocations. This ensures the allocations occur within the client.
+ static void SetClientString ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen );
+ static void SetClientStringVector ( void * clientPtr, XMP_StringPtr* arrayPtr, XMP_Uns32 stringCount );
+
+}; // class TXMPFiles
+
+// =================================================================================================
+
+#endif // __TXMPFiles_hpp__
diff --git a/gpr/source/lib/xmp_core/public/include/TXMPIterator.hpp b/gpr/source/lib/xmp_core/public/include/TXMPIterator.hpp
new file mode 100644
index 0000000..603db68
--- /dev/null
+++ b/gpr/source/lib/xmp_core/public/include/TXMPIterator.hpp
@@ -0,0 +1,235 @@
+#ifndef __TXMPIterator_hpp__
+#define __TXMPIterator_hpp__ 1
+
+#if ( ! __XMP_hpp__ )
+ #error "Do not directly include, use XMP.hpp"
+#endif
+
+// =================================================================================================
+// ADOBE SYSTEMS INCORPORATED
+// Copyright 2002 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.
+// =================================================================================================
+
+// =================================================================================================
+/// \file TXMPIterator.hpp
+/// \brief API for access to the XMP Toolkit iteration services.
+///
+/// \c TXMPIterator is the template class providing iteration services for the XMP Toolkit. It must
+/// be instantiated with a string class such as \c std::string. See the instructions in XMP.hpp, and
+/// the Overview for a discussion of the overall architecture of the XMP API.
+// =================================================================================================
+
+// =================================================================================================
+/// \class TXMPIterator TXMPIterator.hpp
+/// @brief API for access to the XMP Toolkit iteration services.
+///
+/// \c TXMPIterator provides a uniform means to iterate over the schema and properties within an XMP
+/// object. \c TXMPIterator is a template class which must be instantiated with a string class such
+/// as \c std::string. See the instructions in XMP.hpp, and the Overview for a discussion of the
+/// overall architecture of the XMP API. Access these functions through the concrete class,
+/// \c SXMPIterator.
+///
+/// @note Only XMP object iteration is currently available. Future development may include iteration
+/// over global tables, such as registered namespaces.
+///
+/// To understand how iteration works, you should have a thorough understanding of the XMP data
+/// tree, as described in the XMP Specification Part 1. You might also find it helpful to create
+/// some complex XMP and examine the output of \c TXMPMeta::DumpObject().
+///
+/// \li The top of the XMP data tree is a single root node. This does not explicitly appear in the
+/// dump and is never visited by an iterator; that is, it is never returned from
+/// \c TXMPIterator::Next().
+///
+/// \li Beneath the root are schema nodes; these collect the top-level properties in the same
+/// namespace. They are created and destroyed implicitly.
+///
+/// \li Beneath the schema nodes are the property nodes. The nodes below a property node depend on
+/// its type (simple, struct, or array) and whether it has qualifiers.
+///
+/// A \c TXMPIterator constructor defines a starting point for the iteration, and options that
+/// control how it proceeds. By default, iteration starts at the root and visits all nodes beneath
+/// it in a depth-first manner. The root node iteself is not visited; the first visited node is a
+/// schema node. You can provide a schema name or property path to select a different starting node.
+/// By default, this visits the named root node first then all nodes beneath it in a depth-first
+/// manner.
+///
+/// The function \c TXMPIterator::Next() delivers the schema URI, path, and option flags for the
+/// node being visited. If the node is simple, it also delivers the value. Qualifiers for this node
+/// are visited next. The fields of a struct or items of an array are visited after the qualifiers
+/// of the parent.
+///
+/// You can specify options when contructing the iteration object to control how the iteration is
+/// performed.
+///
+/// \li \c #kXMP_IterJustChildren - Visit just the immediate children of the root. Skip the root
+/// itself and all nodes below the immediate children. This omits the qualifiers of the immediate
+/// children, the qualifier nodes being below what they qualify.
+/// \li \c #kXMP_IterJustLeafNodes - Visit just the leaf property nodes and their qualifiers.
+/// \li \c #kXMP_IterJustLeafName - Return just the leaf component of the node names. The default
+/// is to return the full path name.
+/// \li \c #kXMP_IterOmitQualifiers - Do not visit the qualifiers of a node.
+// =================================================================================================
+
+#include "client-glue/WXMPIterator.hpp"
+
+template <class tStringObj> class TXMPIterator {
+
+public:
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Assignment operator, assigns the internal ref and increments the ref count.
+ ///
+ /// Assigns the internal reference from an existing object and increments the reference count on
+ /// the underlying internal XMP iterator.
+ ///
+ /// @param rhs An existing iteration object.
+
+ void operator= ( const TXMPIterator<tStringObj> & rhs );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Copy constructor, creates a client object refering to the same internal object.
+ ///
+ /// Creates a new client iterator that refers to the same underlying iterator as an existing object.
+ ///
+ /// @param original An existing iteration object to copy.
+
+ TXMPIterator ( const TXMPIterator<tStringObj> & original );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Constructs an iterator for properties within a schema in an XMP object.
+ ///
+ /// See the class description for the general operation of an XMP object iterator.
+ /// Overloaded forms are provided to iterate the entire data tree,
+ /// a subtree rooted at a specific node, or properties within a specific schema.
+ ///
+ /// @param xmpObj The XMP object over which to iterate.
+ ///
+ /// @param schemaNS Optional schema namespace URI to restrict the iteration. To visit all of the
+ /// schema, pass 0 or the empty string "".
+ ///
+ /// @param propName Optional property name to restrict the iteration. May be an arbitrary path
+ /// expression. If provided, a schema URI must also be provided. To visit all properties, pass 0
+ /// or the empty string "".
+ ///
+ /// @param options Option flags to control the iteration. A logical OR of these bit flag constants:
+ /// \li \c #kXMP_IterJustChildren - Visit only the immediate children of the root; default visits subtrees.
+ /// \li \c #kXMP_IterJustLeafNodes - Visit only the leaf nodes; default visits all nodes.
+ /// \li \c #kXMP_IterJustLeafName - Return just the leaf part of the path; default returns the full path.
+ /// \li \c #kXMP_IterOmitQualifiers - Omit all qualifiers.
+ ///
+ /// @return The new TXMPIterator object.
+
+ TXMPIterator ( const TXMPMeta<tStringObj> & xmpObj,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_OptionBits options = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Constructs an iterator for a subtree of properties within an XMP object.
+ ///
+ /// See the class description for the general operation of an XMP object iterator. Overloaded
+ /// forms are provided to iterate the entire data tree, a subtree rooted at a specific node, or
+ /// properties within a specific schema.
+ ///
+ /// @param xmpObj The XMP object over which to iterate.
+ ///
+ /// @param schemaNS Optional schema namespace URI to restrict the iteration. To visit all of the
+ /// schema, pass 0 or the empty string "".
+ ///
+ /// @param options Option flags to control the iteration. A logical OR of these bit flag constants:
+ /// \li \c #kXMP_IterJustChildren - Visit only the immediate children of the root; default visits subtrees.
+ /// \li \c #kXMP_IterJustLeafNodes - Visit only the leaf nodes; default visits all nodes.
+ /// \li \c #kXMP_IterJustLeafName - Return just the leaf part of the path; default returns the full path.
+ /// \li \c #kXMP_IterOmitQualifiers - Omit all qualifiers.
+ ///
+ /// @return The new TXMPIterator object.
+
+ TXMPIterator ( const TXMPMeta<tStringObj> & xmpObj,
+ XMP_StringPtr schemaNS,
+ XMP_OptionBits options = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Constructs an iterator for the entire data tree within an XMP object.
+ ///
+ /// See the class description for the general operation of an XMP object iterator. Overloaded
+ /// forms are provided to iterate the entire data tree, a subtree rooted at a specific node, or
+ /// properties within a specific schema.
+ ///
+ /// @param xmpObj The XMP object over which to iterate.
+ ///
+ /// @param options Option flags to control the iteration. A logical OR of these bit flag constants:
+ /// \li \c #kXMP_IterJustChildren - Visit only the immediate children of the root; default visits subtrees.
+ /// \li \c #kXMP_IterJustLeafNodes - Visit only the leaf nodes; default visits all nodes.
+ /// \li \c #kXMP_IterJustLeafName - Return just the leaf part of the path; default returns the full path.
+ /// \li \c #kXMP_IterOmitQualifiers - Omit all qualifiers.
+ ///
+ /// @return The new \c TXMPIterator object.
+
+ TXMPIterator ( const TXMPMeta<tStringObj> & xmpObj,
+ XMP_OptionBits options = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Constructs an iterator for the global tables of the XMP toolkit. Not implemented.
+
+ TXMPIterator ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_OptionBits options );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Destructor, typical virtual destructor.
+
+ virtual ~TXMPIterator() throw();
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c Next() visits the next node in the iteration.
+ ///
+ /// Proceeds to the next node according to the options specified on creation of this object, and
+ /// delivers the schema URI, path, and option flags for the node being visited. If the node is
+ /// simple, it also delivers the value.
+ ///
+ /// @param schemaNS [out] A string object in which to return the assigned the schema namespace
+ /// URI of the current property. Can be null if the value is not wanted.
+ ///
+ /// @param propPath [out] A string object in which to return the XPath name of the current
+ /// property. Can be null if the value is not wanted.
+ ///
+ /// @param propValue [out] A string object in which to return the value of the current
+ /// property. Can be null if the value is not wanted.
+ ///
+ /// @param options [out] A buffer in which to return the flags describing the current property,
+ /// which are a logical OR of \c #XMP_OptionBits bit-flag constants.
+ ///
+ /// @return True if there was another node to visit, false if the iteration is complete.
+
+ bool Next ( tStringObj * schemaNS = 0,
+ tStringObj * propPath = 0,
+ tStringObj * propValue = 0,
+ XMP_OptionBits * options = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c Skip() skips some portion of the remaining iterations.
+ ///
+ /// @param options Option flags to control the iteration, a logical OR of these bit-flag
+ /// constants:
+ /// \li \c #kXMP_IterSkipSubtree - Skip the subtree below the current node.
+ /// \li \c #kXMP_IterSkipSiblings - Skip the subtree below and remaining siblings of the current node.
+
+ void Skip ( XMP_OptionBits options );
+
+private:
+
+ XMPIteratorRef iterRef;
+
+ TXMPIterator(); // ! Hidden, must choose property or table iteration.
+
+ static void SetClientString ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen );
+
+}; // class TXMPIterator
+
+// =================================================================================================
+
+#endif // __TXMPIterator_hpp__
diff --git a/gpr/source/lib/xmp_core/public/include/TXMPMeta.hpp b/gpr/source/lib/xmp_core/public/include/TXMPMeta.hpp
new file mode 100644
index 0000000..57aa62a
--- /dev/null
+++ b/gpr/source/lib/xmp_core/public/include/TXMPMeta.hpp
@@ -0,0 +1,1751 @@
+#ifndef __TXMPMeta_hpp__
+#define __TXMPMeta_hpp__ 1
+
+#if ( ! __XMP_hpp__ )
+ #error "Do not directly include, use XMP.hpp"
+#endif
+
+// =================================================================================================
+// ADOBE SYSTEMS INCORPORATED
+// Copyright 2002 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.
+// =================================================================================================
+
+// =================================================================================================
+/// \file TXMPMeta.hpp
+/// \brief API for access to the XMP Toolkit core services.
+///
+/// \c TXMPMeta is the template class providing the core services of the XMP Toolkit. It must be
+/// instantiated with a string class such as \c std::string. Read the Toolkit Overview for
+/// information about the overall architecture of the XMP API, and the documentation for \c XMP.hpp
+/// for specific instantiation instructions. Please that you MUST NOT derive a class from this class,
+/// consider this class FINAL, use it directly. [1279031]
+///
+/// Access these functions through the concrete class, \c SXMPMeta.
+// =================================================================================================
+
+// =================================================================================================
+/// \class TXMPMeta TXMPMeta.hpp
+/// \brief API for access to the XMP Toolkit core services.
+///
+/// \c TXMPMeta is the template class providing the core services of the XMP Toolkit. It should be
+/// instantiated with a string class such as \c std::string. Read the Toolkit Overview for
+/// information about the overall architecture of the XMP API, and the documentation for \c XMP.hpp
+/// for specific instantiation instructions.
+///
+/// Access these functions through the concrete class, \c SXMPMeta.
+///
+/// You can create \c TXMPMeta objects (also called XMP objects) from metadata that you construct,
+/// or that you obtain from files using the XMP Toolkit's XMPFiles component; see \c TXMPFiles.hpp.
+// =================================================================================================
+
+template <class tStringObj> class TXMPIterator;
+template <class tStringObj> class TXMPUtils;
+
+// -------------------------------------------------------------------------------------------------
+
+template <class tStringObj> class TXMPMeta {
+
+public:
+
+ // =============================================================================================
+ // Initialization and termination
+ // ==============================
+
+ // ---------------------------------------------------------------------------------------------
+ /// \name Initialization and termination
+ ///
+ /// @{
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c GetVersionInfo() retrieves runtime version information.
+ ///
+ /// The header \c XMPVersion.hpp defines a static version number for the XMP Toolkit, which
+ /// describes the version of the API used at client compile time. It is not necessarily the same
+ /// as the runtime version. Do not base runtime decisions on the static version alone; you can,
+ /// however, compare the runtime and static versions.
+ ///
+ /// This function is static; make the call directly from the concrete class (\c SXMPMeta). The
+ /// function can be called before calling \c TXMPMeta::Initialize().
+ ///
+ /// @param info [out] A buffer in which to return the version information.
+
+ static void GetVersionInfo ( XMP_VersionInfo * info );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c Initialize() explicitly initializes the XMP Toolkit before use. */
+
+ /// Initializes the XMP Toolkit.
+ ///
+ /// Call this function before making any other calls to the \c TXMPMeta functions, except
+ /// \c TXMPMeta::GetVersionInfo().
+ ///
+ /// This function is static; make the call directly from the concrete class (\c SXMPMeta).
+ ///
+ /// @return True on success. */
+ static bool Initialize();
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c Terminate() explicitly terminates usage of the XMP Toolkit.
+ ///
+ /// Frees structures created on initialization.
+ ///
+ /// This function is static; make the call directly from the concrete class (\c SXMPMeta).
+
+ static void Terminate();
+
+ /// @}
+
+ // =============================================================================================
+ // Constuctors and destructor
+ // ==========================
+
+ // ---------------------------------------------------------------------------------------------
+ /// \name Constructors and destructor
+ /// @{
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Default constructor, creates an empty object.
+ ///
+ /// The default constructor creates a new empty \c TXMPMeta object.
+ ///
+ /// @return The new object. */
+ TXMPMeta();
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Copy constructor, creates a client object refering to the same internal object.
+ ///
+ /// The copy constructor creates a new \c TXMPMeta object that refers to the same internal XMP
+ /// object. as an existing \c TXMPMeta object.
+ ///
+ /// @param original The object to copy.
+ ///
+ /// @return The new object. */
+
+ TXMPMeta ( const TXMPMeta<tStringObj> & original );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Assignment operator, assigns the internal reference and increments the reference count.
+ ///
+ /// The assignment operator assigns the internal ref from the rhs object and increments the
+ /// reference count on the underlying internal XMP object.
+
+ void operator= ( const TXMPMeta<tStringObj> & rhs );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Reconstructs an XMP object from an internal reference.
+ ///
+ /// This constructor creates a new \c TXMPMeta object that refers to the underlying reference object
+ /// of an existing \c TXMPMeta object. Use to safely pass XMP objects across DLL boundaries.
+ ///
+ /// @param xmpRef The underlying reference object, obtained from some other XMP object with
+ /// \c TXMPMeta::GetInternalRef().
+ ///
+ /// @return The new object.
+
+ TXMPMeta ( XMPMetaRef xmpRef );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Constructs an object and parse one buffer of RDF into it.
+ ///
+ /// This constructor creates a new \c TXMPMeta object and populates it with metadata from a
+ /// buffer containing serialized RDF. This buffer must be a complete RDF parse stream.
+ ///
+ /// The result of passing serialized data to this function is identical to creating an empty
+ /// object then calling \c TXMPMeta::ParseFromBuffer(). To use the constructor, however, the RDF
+ /// must be complete. If you need to parse data from multiple buffers, create an empty object
+ /// and use \c TXMPMeta::ParseFromBuffer().
+ ///
+ /// @param buffer A pointer to the buffer of RDF to be parsed. Can be null if the length is 0;
+ /// in this case, the function creates an empty object.
+ ///
+ /// @param xmpSize The length in bytes of the buffer.
+ ///
+ /// @return The new object.
+
+ TXMPMeta ( XMP_StringPtr buffer,
+ XMP_StringLen xmpSize );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Destructor, typical virtual destructor. */
+ virtual ~TXMPMeta() throw();
+
+ /// @}
+
+ // =============================================================================================
+ // Global state functions
+ // ======================
+
+ // ---------------------------------------------------------------------------------------------
+ /// \name Global option flags
+ /// @{
+ /// Global option flags affect the overall behavior of the XMP Toolkit. The available options
+ /// will be declared in \c XMP_Const.h. There are none in this version of the Toolkit.
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c GetGlobalOptions() retrieves the set of global option flags. There are none in
+ /// this version of the Toolkit.
+ ///
+ /// This function is static; you can make the call from the class without instantiating it.
+ ///
+ /// @return A logical OR of global option bit-flag constants.
+
+ static XMP_OptionBits GetGlobalOptions();
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c SetGlobalOptions() updates the set of global option flags. There are none in this
+ /// version of the Toolkit.
+ ///
+ /// The entire set is replaced with the new values. If only one flag is to be modified, use
+ /// \c TXMPMeta::GetGlobalOptions() to obtain the current set, modify the desired flag, then use
+ /// this function to reset the value.
+ ///
+ /// This function is static; you can make the call from the class without instantiating it.
+ ///
+ /// @param options A logical OR of global option bit-flag constants.
+
+ static void SetGlobalOptions ( XMP_OptionBits options );
+
+ /// @}
+
+ // ---------------------------------------------------------------------------------------------
+ /// \name Internal data structure dump utilities
+ /// @{
+ ///
+ /// These are debugging utilities that dump internal data structures, to be handled by
+ /// client-defined callback described in \c XMP_Const.h.
+ ///
+ /// @see Member function \c TXMPMeta::DumpObject()
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c DumpNamespaces() sends the list of registered namespace URIs and prefixes to a handler.
+ ///
+ /// For debugging. Invokes a client-defined callback for each line of output.
+ ///
+ /// This function is static; make the call directly from the concrete class (\c SXMPMeta).
+ ///
+ /// @param outProc The client-defined procedure to handle each line of output.
+ ///
+ /// @param clientData A pointer to client-defined data to pass to the handler.
+ ///
+ /// @return A success-fail status value, returned from the handler. Zero is success, failure
+ /// values are client-defined.
+
+ static XMP_Status DumpNamespaces ( XMP_TextOutputProc outProc,
+ void * clientData );
+
+ /// @}
+
+ // ---------------------------------------------------------------------------------------------
+ /// \name Namespace Functions
+ /// @{
+ ///
+ /// Namespaces must be registered before use in namespace URI parameters or path expressions.
+ /// Within the XMP Toolkit the registered namespace URIs and prefixes must be unique. Additional
+ /// namespaces encountered when parsing RDF are automatically registered.
+ ///
+ /// The namespace URI should always end in an XML name separator such as '/' or '#'. This is
+ /// because some forms of RDF shorthand catenate a namespace URI with an element name to form a
+ /// new URI.
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c RegisterNamespace() registers a namespace URI with a suggested prefix.
+ ///
+ /// If the URI is not registered but the suggested prefix is in use, a unique prefix is created
+ /// from the suggested one. The actual registered prefix is returned. The function result tells
+ /// if the registered prefix is the suggested one. It is not an error if the URI is already
+ /// registered, regardless of the prefix.
+ ///
+ /// This function is static; make the call directly from the concrete class (\c SXMPMeta).
+ ///
+ /// @param namespaceURI The URI for the namespace. Must be a valid XML URI.
+ ///
+ /// @param suggestedPrefix The suggested prefix to be used if the URI is not yet registered.
+ /// Must be a valid XML name.
+ ///
+ /// @param registeredPrefix [out] A string object in which to return the prefix actually
+ /// registered for this URI.
+ ///
+ /// @return True if the registered prefix matches the suggested prefix.
+ ///
+ /// @note No checking is done on either the URI or the prefix. */
+
+ static bool RegisterNamespace ( XMP_StringPtr namespaceURI,
+ XMP_StringPtr suggestedPrefix,
+ tStringObj * registeredPrefix );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c GetNamespacePrefix() obtains the prefix for a registered namespace URI, and
+ /// reports whether the URI is registered.
+ ///
+ /// This function is static; make the call directly from the concrete class (\c SXMPMeta).
+ ///
+ /// @param namespaceURI The URI for the namespace. Must not be null or the empty string. It is
+ /// not an error if the namespace URI is not registered.
+ ///
+ /// @param namespacePrefix [out] A string object in which to return the prefix registered for
+ /// this URI, with a terminating colon character, ':'. If the namespace is not registered, this
+ /// string is not modified.
+ ///
+ /// @return True if the namespace URI is registered.
+
+ static bool GetNamespacePrefix ( XMP_StringPtr namespaceURI,
+ tStringObj * namespacePrefix );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c GetNamespaceURI() obtains the URI for a registered namespace prefix, and reports
+ /// whether the prefix is registered.
+ ///
+ /// This function is static; make the call directly from the concrete class (\c SXMPMeta).
+ ///
+ /// @param namespacePrefix The prefix for the namespace. Must not be null or the empty string.
+ /// It is not an error if the namespace prefix is not registered.
+ ///
+ /// @param namespaceURI [out] A string object in which to return the URI registered for this
+ /// prefix. If the prefix is not registered, this string is not modified.
+ ///
+ /// @return True if the namespace prefix is registered.
+
+ static bool GetNamespaceURI ( XMP_StringPtr namespacePrefix,
+ tStringObj * namespaceURI );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Not implemented.
+ ///
+ /// Deletes a namespace from the registry. Does nothing if the URI is not registered, or if the
+ /// parameter is null or the empty string.
+ ///
+ /// This function is static; make the call directly from the concrete class (\c SXMPMeta).
+ ///
+ /// @param namespaceURI The URI for the namespace.
+
+ static void DeleteNamespace ( XMP_StringPtr namespaceURI );
+
+ /// @}
+
+ // =============================================================================================
+ // Basic property manipulation functions
+ // =====================================
+
+ // *** Should add discussion of schemaNS and propName prefix usage.
+
+ // ---------------------------------------------------------------------------------------------
+ /// \name Accessing property values
+ /// @{
+ ///
+ /// The property value accessors all take a property specification; the top level namespace URI
+ /// (the "schema" namespace) and the basic name of the property being referenced. See the
+ /// introductory discussion of path expression usage for more information.
+ ///
+ /// The accessor functions return true if the specified property exists. If it does, output
+ /// parameters return the value (if any) and option flags describing the property. The option
+ /// bit-flag constants that describe properties are \c kXMP_PropXx and
+ /// \c kXMP_ArrayIsXx. See \c #kXMP_PropValueIsURI and following, and macros \c #XMP_PropIsSimple
+ /// and following in \c XMP_Const.h. If the property exists and has a value, it is returned as a
+ /// Unicode string in UTF-8 encoding. Arrays and the non-leaf levels of structs do not have
+ /// values.
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c GetProperty() reports whether a property exists, and retrieves its value.
+ ///
+ /// This is the simplest property accessor. Use this to retrieve the values of top-level simple
+ /// properties, or after using the path composition functions in \c TXMPUtils.
+ ///
+ /// When specifying a namespace and path (in this and all other accessors):
+ /// \li If a namespace URI is specified, it must be for a registered namespace.
+ /// \li If the namespace is specified only by a prefix in the property name path,
+ /// it must be a registered prefix.
+ /// \li If both a URI and path prefix are present, they must be corresponding
+ /// parts of a registered namespace.
+ ///
+ /// @param schemaNS The namespace URI for the property. The URI must be for a registered
+ /// namespace. Must not be null or the empty string.
+ ///
+ /// @param propName The name of the property. Can be a general path expression, must not be null
+ /// or the empty string. The first component can be a namespace prefix; if present without a
+ /// \c schemaNS value, the prefix specifies the namespace. The prefix must be for a registered
+ /// namespace, and if a namespace URI is specified, must match the registered prefix for that
+ /// namespace.
+ ///
+ /// @param propValue [out] A string object in which to return the value of the property, if the
+ /// property exists and has a value. Arrays and non-leaf levels of structs do not have values.
+ /// Can be null if the value is not wanted.
+ ///
+ /// @param options A buffer in which to return option flags describing the property. Can be null
+ /// if the flags are not wanted.
+ ///
+ /// @return True if the property exists.
+
+ bool GetProperty ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ tStringObj * propValue,
+ XMP_OptionBits * options ) const;
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c GetArrayItem() provides access to items within an array.
+ ///
+ /// Reports whether the item exists; if it does, and if it has a value, the function retrieves
+ /// the value. Items are accessed by an integer index, where the first item has index 1.
+ ///
+ /// @param schemaNS The namespace URI for the array; see \c GetProperty().
+ ///
+ /// @param arrayName The name of the array. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param itemIndex The 1-based index of the desired item. Use the macro \c #kXMP_ArrayLastItem
+ /// to specify the last existing array item.
+ ///
+ /// @param itemValue [out] A string object in which to return the value of the array item, if it
+ /// has a value. Arrays and non-leaf levels of structs do not have values. Can be null if the
+ /// value is not wanted.
+ ///
+ /// @param options [out] A buffer in which to return the option flags describing the array item.
+ /// Can be null if the flags are not wanted.
+ ///
+ /// @return True if the array item exists.
+
+ bool GetArrayItem ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex,
+ tStringObj * itemValue,
+ XMP_OptionBits * options ) const;
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c GetStructField() provides access to fields within a nested structure.
+ ///
+ /// Reports whether the field exists; if it does, and if it has a value, the function retrieves
+ /// the value.
+ ///
+ /// @param schemaNS The namespace URI for the struct; see \c GetProperty().
+ ///
+ /// @param structName The name of the struct. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param fieldNS The namespace URI for the field. Same URI and prefix usage as the \c schemaNS
+ /// and \c structName parameters.
+ ///
+ /// @param fieldName The name of the field. Must be a single XML name, must not be null or the
+ /// empty string. Same URI and prefix usage as the \c schemaNS and \c structName parameters.
+ ///
+ /// @param fieldValue [out] A string object in which to return the value of the field, if the
+ /// field has a value. Arrays and non-leaf levels of structs do not have values. Can be null if
+ /// the value is not wanted.
+ ///
+ /// @param options [out] A buffer in which to return the option flags describing the field. Can
+ /// be null if the flags are not wanted.
+ ///
+ /// @return True if the field exists.
+
+ bool GetStructField ( XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ tStringObj * fieldValue,
+ XMP_OptionBits * options ) const;
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c GetQualifier() provides access to a qualifier attached to a property.
+ ///
+ /// @note In this version of the Toolkit, qualifiers are supported only for simple leaf properties.
+ ///
+ /// @param schemaNS The namespace URI; see \c GetProperty().
+ ///
+ /// @param propName The name of the property to which the qualifier is attached. Can be a
+ /// general path expression, must not be null or the empty string; see \c GetProperty() for
+ /// namespace prefix usage.
+ ///
+ /// @param qualNS The namespace URI for the qualifier. Same URI and prefix usage as the
+ /// \c schemaNS and \c propName parameters.
+ ///
+ /// @param qualName The name of the qualifier. Must be a single XML name, must not be null or
+ /// the empty string. Same URI and prefix usage as the \c schemaNS and \c propName parameters.
+ ///
+ /// @param qualValue [out] A string object in which to return the value of the qualifier, if the
+ /// qualifier has a value. Arrays and non-leaf levels of structs do not have values. Can be null
+ /// if the value is not wanted.
+ ///
+ /// @param options [out] A buffer in which to return the option flags describing the qualifier.
+ /// Can be null if the flags are not wanted.
+ ///
+ /// @return True if the qualifier exists.
+
+ bool GetQualifier ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName,
+ tStringObj * qualValue,
+ XMP_OptionBits * options ) const;
+
+ /// @}
+
+ // =============================================================================================
+
+ // ---------------------------------------------------------------------------------------------
+ /// \name Creating properties and setting their values
+ /// @{
+ ///
+ /// These functions all take a property specification; the top level namespace URI (the "schema"
+ /// namespace) and the basic name of the property being referenced. See the introductory
+ /// discussion of path expression usage for more information.
+ ///
+ /// All of the functions take a UTF-8 encoded Unicode string for the property value. Arrays and
+ /// non-leaf levels of structs do not have values. The value can be passed as an
+ /// \c #XMP_StringPtr (a pointer to a null-terminated string), or as a string object
+ /// (\c tStringObj).
+
+ /// Each function takes an options flag that describes the property. You can use these functions
+ /// to create empty arrays and structs by setting appropriate option flags. When you assign a
+ /// value, all levels of a struct that are implicit in the assignment are created if necessary.
+ /// \c TXMPMeta::AppendArrayItem() implicitly creates the named array if necessary.
+ ///
+ /// The allowed option bit-flags include:
+ /// \li \c #kXMP_PropValueIsStruct - Can be used to create an empty struct.
+ /// A struct is implicitly created when the first field is set.
+ /// \li \c #kXMP_PropValueIsArray - By default, a general unordered array (bag).
+ /// \li \c #kXMP_PropArrayIsOrdered - An ordered array.
+ /// \li \c #kXMP_PropArrayIsAlternate - An alternative array.
+ /// \li \c #kXMP_PropArrayIsAltText - An alt-text array. Each array element must
+ /// be a simple property with an \c xml:lang attribute.
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c SetProperty() creates or sets a property value.
+ ///
+ /// This is the simplest property setter. Use it for top-level simple properties, or after using
+ /// the path composition functions in \c TXMPUtils.
+ ///
+ /// @param schemaNS The namespace URI; see \c GetProperty().
+ ///
+ /// @param propName The name of the property. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param propValue The new value, a pointer to a null terminated UTF-8 string. Must be null
+ /// for arrays and non-leaf levels of structs that do not have values.
+ ///
+ /// @param options Option flags describing the property; a logical OR of allowed bit-flag
+ /// constants; see \c #kXMP_PropValueIsStruct and following. Must match the type of a property
+ /// that already exists.
+
+ void SetProperty ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr propValue,
+ XMP_OptionBits options = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c SetProperty() creates or sets a property value using a string object.
+ ///
+ /// Overloads the basic form of the function, allowing you to pass a string object
+ /// for the item value. It is otherwise identical; see details in the canonical form.
+
+ void SetProperty ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ const tStringObj & propValue,
+ XMP_OptionBits options = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c SetArrayItem() creates or sets the value of an item within an array.
+ ///
+ /// Items are accessed by an integer index, where the first item has index 1. This function
+ /// creates the item if necessary, but the array itself must already exist Use
+ /// \c AppendArrayItem() to create arrays. A new item is automatically appended if the index is the
+ /// array size plus 1. To insert a new item before or after an existing item, use option flags.
+ ///
+ /// Use \c TXMPUtils::ComposeArrayItemPath() to create a complex path.
+ ///
+ /// @param schemaNS The namespace URI; see \c GetProperty().
+ ///
+ /// @param arrayName The name of the array. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param itemIndex The 1-based index of the desired item. Use the macro \c #kXMP_ArrayLastItem
+ /// to specify the last existing array item.
+ ///
+ /// @param itemValue The new item value, a null-terminated UTF-8 string, if the array item has a
+ /// value.
+ ///
+ /// @param options Option flags describing the array type and insertion location for a new item;
+ /// a logical OR of allowed bit-flag constants. The type, if specified, must match the existing
+ /// array type, \c #kXMP_PropArrayIsOrdered, \c #kXMP_PropArrayIsAlternate, or
+ /// \c #kXMP_PropArrayIsAltText. Default (0 or \c #kXMP_NoOptions) matches the existing array type.
+ ///
+ /// To insert a new item before or after the specified index, set flag \c #kXMP_InsertBeforeItem
+ /// or \c #kXMP_InsertAfterItem.
+
+ void SetArrayItem ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex,
+ XMP_StringPtr itemValue,
+ XMP_OptionBits options = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c SetArrayItem() creates or sets the value of an item within an array using a string object.
+ ///
+ /// Overloads the basic form of the function, allowing you to pass a string object in which to
+ /// return the item value. It is otherwise identical; see details in the canonical form.
+
+ void SetArrayItem ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex,
+ const tStringObj & itemValue,
+ XMP_OptionBits options = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c AppendArrayItem() adds an item to an array, creating the array if necessary.
+ ///
+ /// This function simplifies construction of an array by not requiring that you pre-create an
+ /// empty array. The array that is assigned is created automatically if it does not yet exist.
+ /// If the array exists, it must have the form specified by the options. Each call appends a new
+ /// item to the array.
+ ///
+ /// Use \c TXMPUtils::ComposeArrayItemPath() to create a complex path.
+ ///
+ /// @param schemaNS The namespace URI; see \c GetProperty().
+ ///
+ /// @param arrayName The name of the array. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param arrayOptions Option flags describing the array type to create; a logical OR of
+ /// allowed bit-flag constants, \c #kXMP_PropArrayIsOrdered, \c #kXMP_PropArrayIsAlternate, or
+ /// \c #kXMP_PropArrayIsAltText. If the array exists, must match the existing array type or be
+ /// null (0 or \c #kXMP_NoOptions).
+ ///
+ /// @param itemValue The new item value, a null-terminated UTF-8 string, if the array item has a
+ /// value.
+ ///
+ /// @param itemOptions Option flags describing the item type to create; one of the bit-flag
+ /// constants \c #kXMP_PropValueIsArray or \c #kXMP_PropValueIsStruct to create a complex array
+ /// item.
+
+ void AppendArrayItem ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_OptionBits arrayOptions,
+ XMP_StringPtr itemValue,
+ XMP_OptionBits itemOptions = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c AppendArrayItem() adds an item to an array using a string object value, creating
+ /// the array if necessary.
+ ///
+ /// Overloads the basic form of the function, allowing you to pass a string object in which to
+ /// return the item value. It is otherwise identical; see details in the canonical form.
+
+ void AppendArrayItem ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_OptionBits arrayOptions,
+ const tStringObj & itemValue,
+ XMP_OptionBits itemOptions = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c SetStructField() creates or sets the value of a field within a nested structure.
+ ///
+ /// Use this to set a value within an existing structure, create a new field within an existing
+ /// structure, or create an empty structure of any depth. If you set a field in a structure that
+ /// does not exist, the structure is automatically created.
+ ///
+ /// Use \c TXMPUtils::ComposeStructFieldPath() to create a complex path.
+ ///
+ /// @param schemaNS The namespace URI; see \c GetProperty().
+ ///
+ /// @param structName The name of the struct. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param fieldNS The namespace URI for the field. Same namespace and prefix usage as
+ /// \c GetProperty().
+ ///
+ /// @param fieldName The name of the field. Must be a single XML name, must not be null or the
+ /// empty string. Same namespace and prefix usage as \c GetProperty().
+ ///
+ /// @param fieldValue The new value, a null-terminated UTF-8 string, if the field has a value.
+ /// Null to create a new, empty struct or empty field in an existing struct.
+ ///
+ /// @param options Option flags describing the property, in which the bit-flag
+ /// \c #kXMP_PropValueIsStruct must be set to create a struct.
+
+ void SetStructField ( XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ XMP_StringPtr fieldValue,
+ XMP_OptionBits options = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c SetStructField() creates or sets the value of a field within a nested structure,
+ /// using a string object.
+ ///
+ /// Overloads the basic form of the function, allowing you to pass a string object in which to
+ /// return the field value. It is otherwise identical; see details in the canonical form.
+
+ void SetStructField ( XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ const tStringObj & fieldValue,
+ XMP_OptionBits options = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c SetQualifier() creates or sets a qualifier attached to a property.
+ ///
+ /// Use this to set a value for an existing qualifier, or create a new qualifier. <<how do
+ /// options work? macro vs bit-flag? interaction w/XMP_PropHasQualifier?>> Use
+ /// \c TXMPUtils::ComposeQualifierPath() to create a complex path.
+ ///
+ /// @param schemaNS The namespace URI; see \c GetProperty().
+ ///
+ /// @param propName The name of the property to which the qualifier is attached. Can be a
+ /// general path expression, must not be null or the empty string; see \c GetProperty() for
+ /// namespace prefix usage.
+ ///
+ /// @param qualNS The namespace URI for the qualifier. Same namespace and prefix usage as
+ /// \c GetProperty().
+ ///
+ /// @param qualName The name of the qualifier. Must be a single XML name, must not be null or
+ /// the empty string. Same namespace and prefix usage as \c GetProperty().
+ ///
+ /// @param qualValue The new value, a null-terminated UTF-8 string, if the qualifier has a
+ /// value. Null to create a new, empty qualifier.
+ ///
+ /// @param options Option flags describing the <<qualified property? qualifier?>>, a logical OR
+ /// of property-type bit-flag constants. Use the macro \c #XMP_PropIsQualifier to create a
+ /// qualifier. <<??>>
+
+ void SetQualifier ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName,
+ XMP_StringPtr qualValue,
+ XMP_OptionBits options = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c SetQualifier() creates or sets a qualifier attached to a property using a string object.
+ ///
+ /// Overloads the basic form of the function, allowing you to pass a string object
+ /// for the qualifier value. It is otherwise identical; see details in the canonical form.
+
+ void SetQualifier ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName,
+ const tStringObj & qualValue,
+ XMP_OptionBits options = 0 );
+
+ /// @}
+
+ // =============================================================================================
+
+ // ---------------------------------------------------------------------------------------------
+ /// \name Detecting and deleting properties.
+ /// @{
+ ///
+ /// The namespace URI and prefix usage for property specifiers in these functions is the same as
+ /// for \c TXMPMeta::GetProperty().
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c DeleteProperty() deletes an XMP subtree rooted at a given property.
+ ///
+ /// It is not an error if the property does not exist.
+ ///
+ /// @param schemaNS The namespace URI for the property; see \c GetProperty().
+ ///
+ /// @param propName The name of the property; see \c GetProperty().
+
+ void DeleteProperty ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c DeleteArrayItem() deletes an XMP subtree rooted at a given array item.
+ ///
+ /// It is not an error if the array item does not exist. Use
+ /// \c TXMPUtils::ComposeArrayItemPath() to create a complex path.
+ ///
+ /// @param schemaNS The namespace URI for the array; see \c GetProperty().
+ ///
+ /// @param arrayName The name of the array. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param itemIndex The 1-based index of the desired item. Use the macro \c #kXMP_ArrayLastItem
+ /// to specify the last existing array item.
+
+ void DeleteArrayItem ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c DeleteStructField() deletes an XMP subtree rooted at a given struct field.
+ ///
+ /// It is not an error if the field does not exist.
+ ///
+ /// @param schemaNS The namespace URI for the struct; see \c GetProperty().
+ ///
+ /// @param structName The name of the struct. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param fieldNS The namespace URI for the field. Same namespace and prefix usage as
+ /// \c GetProperty().
+ ///
+ /// @param fieldName The name of the field. Must be a single XML name, must not be null or the
+ /// empty string. Same namespace and prefix usage as \c GetProperty().
+
+ void DeleteStructField ( XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c DeleteQualifier() deletes an XMP subtree rooted at a given qualifier.
+ ///
+ /// It is not an error if the qualifier does not exist.
+ ///
+ /// @param schemaNS The namespace URI; see \c GetProperty().
+ ///
+ /// @param propName The name of the property to which the qualifier is attached. Can be a
+ /// general path expression, must not be null or the empty string; see \c GetProperty() for
+ /// namespace prefix usage.
+ ///
+ /// @param qualNS The namespace URI for the qualifier. Same namespace and prefix usage as
+ /// \c GetProperty().
+ ///
+ /// @param qualName The name of the qualifier. Must be a single XML name, must not be null or
+ /// the empty string. Same namespace and prefix usage as \c GetProperty().
+
+ void DeleteQualifier ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c DoesPropertyExist() reports whether a property currently exists.
+ ///
+ /// @param schemaNS The namespace URI for the property; see \c GetProperty().
+ ///
+ /// @param propName The name of the property; see \c GetProperty().
+ ///
+ /// @return True if the property exists.
+
+ bool DoesPropertyExist ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName ) const;
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c DoesArrayItemExist() reports whether an array item currently exists.
+ ///
+ /// Use \c TXMPUtils::ComposeArrayItemPath() to create a complex path.
+ ///
+ /// @param schemaNS The namespace URI; see \c GetProperty().
+ ///
+ /// @param arrayName The name of the array. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param itemIndex The 1-based index of the desired item. Use the macro \c #kXMP_ArrayLastItem
+ /// to specify the last existing array item.
+ ///
+ /// @return True if the array item exists.
+
+ bool DoesArrayItemExist ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex ) const;
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c DoesStructFieldExist() reports whether a struct field currently exists.
+ ///
+ /// Use \c TXMPUtils::ComposeStructFieldPath() to create a complex path.
+ ///
+ /// @param schemaNS The namespace URI; see \c GetProperty().
+ ///
+ /// @param structName The name of the struct. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param fieldNS The namespace URI for the field. Same namespace and prefix usage as
+ /// \c GetProperty().
+ ///
+ /// @param fieldName The name of the field. Must be a single XML name, must not be null or the
+ /// empty string. Same namespace and prefix usage as \c GetProperty().
+ ///
+ /// @return True if the field exists.
+
+ bool DoesStructFieldExist ( XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName ) const;
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c DoesQualifierExist() reports whether a qualifier currently exists.
+ ///
+ /// @param schemaNS The namespace URI; see \c GetProperty().
+ ///
+ /// @param propName The name of the property to which the qualifier is attached. Can be a
+ /// general path expression, must not be null or the empty string; see \c GetProperty() for
+ /// namespace prefix usage.
+ ///
+ /// @param qualNS The namespace URI for the qualifier. Same namespace and prefix usage as
+ /// \c GetProperty().
+ ///
+ /// @param qualName The name of the qualifier. Must be a single XML name, must not be null or
+ /// the empty string. Same namespace and prefix usage as \c GetProperty().
+ ///
+ /// @return True if the qualifier exists.
+
+ bool DoesQualifierExist ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName ) const;
+
+ /// @}
+
+ // =============================================================================================
+ // Specialized Get and Set functions
+ // =============================================================================================
+
+ // ---------------------------------------------------------------------------------------------
+ /// \name Accessing properties as binary values.
+ /// @{
+ ///
+ /// These are very similar to \c TXMPMeta::GetProperty() and \c TXMPMeta::SetProperty(), except
+ /// that the value is returned or provided in binary form instead of as a UTF-8 string.
+ /// \c TXMPUtils provides functions for converting between binary and string values.
+ /// Use the path composition functions in \c TXMPUtils to compose complex path expressions
+ /// for fields or items in nested structures or arrays, or for qualifiers.
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c GetProperty_Bool() retrieves the value of a Boolean property as a C++ bool.
+ ///
+ /// Reports whether a property exists, and retrieves its binary value and property type information.
+ ///
+ /// @param schemaNS The namespace URI; see \c GetProperty().
+ ///
+ /// @param propName The name of the property. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param propValue [out] A buffer in which to return the binary value. Can be null if the
+ /// value is not wanted. Must be null for arrays and non-leaf levels of structs that do not have
+ /// values.
+ ///
+ /// @param options [out] A buffer in which to return the option flags describing the property, a
+ /// logical OR of allowed bit-flag constants; see \c #kXMP_PropValueIsStruct and following. Can
+ /// be null if flags are not wanted.
+ ///
+ /// @return True if the property exists.
+
+ bool GetProperty_Bool ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ bool * propValue,
+ XMP_OptionBits * options ) const;
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c GetProperty_Int() retrieves the value of an integer property as a C long integer.
+ ///
+ /// Reports whether a property exists, and retrieves its binary value and property type information.
+ ///
+ /// @param schemaNS The namespace URI; see \c GetProperty().
+ ///
+ /// @param propName The name of the property. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param propValue [out] A buffer in which to return the binary value. Can be null if the
+ /// value is not wanted. Must be null for arrays and non-leaf levels of structs that do not have
+ /// values.
+ ///
+ /// @param options [out] A buffer in which to return the option flags describing the property, a
+ /// logical OR of allowed bit-flag constants; see \c #kXMP_PropValueIsStruct and following. Can
+ /// be null if flags are not wanted.
+ ///
+ /// @return True if the property exists.
+
+ bool GetProperty_Int ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_Int32 * propValue,
+ XMP_OptionBits * options ) const;
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c GetProperty_Int64() retrieves the value of an integer property as a C long long integer.
+ ///
+ /// Reports whether a property exists, and retrieves its binary value and property type information.
+ ///
+ /// @param schemaNS The namespace URI; see \c GetProperty().
+ ///
+ /// @param propName The name of the property. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param propValue [out] A buffer in which to return the binary value. Can be null if the
+ /// value is not wanted. Must be null for arrays and non-leaf levels of structs that do not have
+ /// values.
+ ///
+ /// @param options [out] A buffer in which to return the option flags describing the property, a
+ /// logical OR of allowed bit-flag constants; see \c #kXMP_PropValueIsStruct and following. Can
+ /// be null if flags are not wanted.
+ ///
+ /// @return True if the property exists.
+
+ bool GetProperty_Int64 ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_Int64 * propValue,
+ XMP_OptionBits * options ) const;
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c GetProperty_Float() retrieves the value of a floating-point property as a C double float.
+ ///
+ /// Reports whether a property exists, and retrieves its binary value and property type information.
+ ///
+ /// @param schemaNS The namespace URI; see \c GetProperty().
+ ///
+ /// @param propName The name of the property. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param propValue [out] A buffer in which to return the binary value. Can be null if the
+ /// value is not wanted. Must be null for arrays and non-leaf levels of structs that do not have
+ /// values.
+ ///
+ /// @param options [out] A buffer in which to return the option flags describing the property, a
+ /// logical OR of allowed bit-flag constants; see \c #kXMP_PropValueIsStruct and following. Can
+ /// be null if flags are not wanted.
+ ///
+ /// @return True if the property exists.
+
+ bool GetProperty_Float ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ double * propValue,
+ XMP_OptionBits * options ) const;
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c GetProperty_Date() retrieves the value of a date-time property as an \c #XMP_DateTime structure.
+ ///
+ /// Reports whether a property exists, and retrieves its binary value and property type information.
+ ///
+ /// @param schemaNS The namespace URI; see \c GetProperty().
+ ///
+ /// @param propName The name of the property. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param propValue [out] A buffer in which to return the binary value. Can be null if the
+ /// value is not wanted. Must be null for arrays and non-leaf levels of structs that do not have
+ /// values.
+ ///
+ /// @param options [out] A buffer in which to return the option flags describing the property, a
+ /// logical OR of allowed bit-flag constants; see \c #kXMP_PropValueIsStruct and following. Can
+ /// be null if flags are not wanted.
+ ///
+ /// @return True if the property exists.
+
+ bool GetProperty_Date ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_DateTime * propValue,
+ XMP_OptionBits * options ) const;
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c SetProperty_Bool() sets the value of a Boolean property using a C++ bool.
+ ///
+ /// Sets a property with a binary value, creating it if necessary.
+ ///
+ /// @param schemaNS The namespace URI; see \c GetProperty().
+ ///
+ /// @param propName The name of the property. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param propValue The new binary value. Can be null if creating the property. Must be null
+ /// for arrays and non-leaf levels of structs that do not have values.
+ ///
+ /// @param options Option flags describing the property; a logical OR of allowed bit-flag
+ /// constants; see \c #kXMP_PropValueIsStruct and following. Must match the type of a property
+ /// that already exists.
+
+ void SetProperty_Bool ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ bool propValue,
+ XMP_OptionBits options = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c SetProperty_Int() sets the value of an integer property using a C long integer.
+ ///
+ /// Sets a property with a binary value, creating it if necessary.
+ ///
+ /// @param schemaNS The namespace URI; see \c GetProperty().
+ ///
+ /// @param propName The name of the property. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param propValue The new binary value. Can be null if creating the property. Must be null
+ /// for arrays and non-leaf levels of structs that do not have values.
+ ///
+ /// @param options Option flags describing the property; a logical OR of allowed bit-flag
+ /// constants; see \c #kXMP_PropValueIsStruct and following. Must match the type of a property
+ /// that already exists.
+
+ void SetProperty_Int ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_Int32 propValue,
+ XMP_OptionBits options = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c SetProperty_Int64() sets the value of an integer property using a C long long integer.
+ ///
+ /// Sets a property with a binary value, creating it if necessary.
+ ///
+ /// @param schemaNS The namespace URI; see \c GetProperty().
+ ///
+ /// @param propName The name of the property. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param propValue The new binary value. Can be null if creating the property. Must be null
+ /// for arrays and non-leaf levels of structs that do not have values.
+ ///
+ /// @param options Option flags describing the property; a logical OR of allowed bit-flag
+ /// constants; see \c #kXMP_PropValueIsStruct and following. Must match the type of a property
+ /// that already exists.
+
+ void SetProperty_Int64 ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_Int64 propValue,
+ XMP_OptionBits options = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c SetProperty_Float() sets the value of a floating-point property using a C double float.
+ ///
+ /// Sets a property with a binary value, creating it if necessary.
+ ///
+ /// @param schemaNS The namespace URI; see \c GetProperty().
+ ///
+ /// @param propName The name of the property. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param propValue The new binary value. Can be null if creating the property. Must be null
+ /// for arrays and non-leaf levels of structs that do not have values.
+ ///
+ /// @param options Option flags describing the property; a logical OR of allowed bit-flag
+ /// constants; see \c #kXMP_PropValueIsStruct and following. Must match the type of a property
+ /// that already exists.
+
+ void SetProperty_Float ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ double propValue,
+ XMP_OptionBits options = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c SetProperty_Date() sets the value of a date/time property using an \c #XMP_DateTime structure.
+ ///
+ /// Sets a property with a binary value, creating it if necessary.
+ ///
+ /// @param schemaNS The namespace URI; see \c GetProperty().
+ ///
+ /// @param propName The name of the property. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param propValue The new binary value. Can be null if creating the property. Must be null
+ /// for arrays and non-leaf levels of structs that do not have values.
+ ///
+ /// @param options Option flags describing the property; a logical OR of allowed bit-flag
+ /// constants; see \c #kXMP_PropValueIsStruct and following. Must match the type of a property
+ /// that already exists.
+
+ void SetProperty_Date ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ const XMP_DateTime & propValue,
+ XMP_OptionBits options = 0 );
+
+ /// @}
+ // =============================================================================================
+ /// \name Accessing localized text (alt-text) properties.
+ /// @{
+ ///
+ /// Localized text properties are stored in alt-text arrays. They allow multiple concurrent
+ /// localizations of a property value, for example a document title or copyright in several
+ /// languages.
+ ///
+ /// These functions provide convenient support for localized text properties, including a
+ /// number of special and obscure aspects. The most important aspect of these functions is that
+ /// they select an appropriate array item based on one or two RFC 3066 language tags. One of
+ /// these languages, the "specific" language, is preferred and selected if there is an exact
+ /// match. For many languages it is also possible to define a "generic" language that can be
+ /// used if there is no specific language match. The generic language must be a valid RFC 3066
+ /// primary subtag, or the empty string.
+ ///
+ /// For example, a specific language of "en-US" should be used in the US, and a specific
+ /// language of "en-UK" should be used in England. It is also appropriate to use "en" as the
+ /// generic language in each case. If a US document goes to England, the "en-US" title is
+ /// selected by using the "en" generic language and the "en-UK" specific language.
+ ///
+ /// It is considered poor practice, but allowed, to pass a specific language that is just an
+ /// RFC 3066 primary tag. For example "en" is not a good specific language, it should only be
+ /// used as a generic language. Passing "i" or "x" as the generic language is also considered
+ /// poor practice but allowed.
+ ///
+ /// Advice from the W3C about the use of RFC 3066 language tags can be found at:
+ /// \li http://www.w3.org/International/articles/language-tags/
+ ///
+ /// \note RFC 3066 language tags must be treated in a case insensitive manner. The XMP toolkit
+ /// does this by normalizing their capitalization:
+ /// \li The primary subtag is lower case, the suggested practice of ISO 639.
+ /// \li All 2 letter secondary subtags are upper case, the suggested practice of ISO 3166.
+ /// \li All other subtags are lower case.
+ ///
+ /// The XMP specification defines an artificial language, "x-default", that is used to
+ /// explicitly denote a default item in an alt-text array. The XMP toolkit normalizes alt-text
+ /// arrays such that the x-default item is the first item. The \c SetLocalizedText() function
+ /// has several special features related to the x-default item, see its description for details.
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c GetLocalizedText() retrieves information about a selected item in an alt-text array.
+ ///
+ /// The array item is selected according to these rules:
+ /// \li Look for an exact match with the specific language.
+ /// \li If a generic language is given, look for a partial match.
+ /// \li Look for an x-default item.
+ /// \li Choose the first item.
+ ///
+ /// A partial match with the generic language is where the start of the item's language matches
+ /// the generic string and the next character is '-'. An exact match is also recognized as a
+ /// degenerate case.
+ ///
+ /// You can pass "x-default" as the specific language. In this case, selection of an
+ /// \c x-default item is an exact match by the first rule, not a selection by the 3rd rule. The
+ /// last 2 rules are fallbacks used when the specific and generic languages fail to produce a
+ /// match.
+ ///
+ /// The return value reports whether a match was successfully made.
+ ///
+ /// @param schemaNS The namespace URI for the alt-text array; see \c GetProperty().
+ ///
+ /// @param altTextName The name of the alt-text array. Can be a general path expression, must
+ /// not be null or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param genericLang The name of the generic language as an RFC 3066 primary subtag. Can be
+ /// null or the empty string if no generic language is wanted.
+ ///
+ /// @param specificLang The name of the specific language as an RFC 3066 tag, or "x-default".
+ /// Must not be null or the empty string.
+ ///
+ /// @param actualLang [out] A string object in which to return the language of the selected
+ /// array item, if an appropriate array item is found. Can be null if the language is not wanted.
+ ///
+ /// @param itemValue [out] A string object in which to return the value of the array item, if an
+ /// appropriate array item is found. Can be null if the value is not wanted.
+ ///
+ /// @param options A buffer in which to return the option flags that describe the array item, if
+ /// an appropriate array item is found. Can be null if the flags are not wanted.
+ ///
+ /// @return True if an appropriate array item exists.
+
+ bool GetLocalizedText ( XMP_StringPtr schemaNS,
+ XMP_StringPtr altTextName,
+ XMP_StringPtr genericLang,
+ XMP_StringPtr specificLang,
+ tStringObj * actualLang,
+ tStringObj * itemValue,
+ XMP_OptionBits * options ) const;
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c SetLocalizedText() modifies the value of a selected item in an alt-text array.
+ ///
+ /// Creates an appropriate array item if necessary, and handles special cases for the x-default
+ /// item.
+ ///
+ /// The array item is selected according to these rules:
+ /// \li Look for an exact match with the specific language.
+ /// \li If a generic language is given, look for a partial match.
+ /// \li Look for an x-default item.
+ /// \li Choose the first item.
+ ///
+ /// A partial match with the generic language is where the start of the item's language matches
+ /// the generic string and the next character is '-'. An exact match is also recognized as a
+ /// degenerate case.
+ ///
+ /// You can pass "x-default" as the specific language. In this case, selection of an
+ /// \c x-default item is an exact match by the first rule, not a selection by the 3rd rule. The
+ /// last 2 rules are fallbacks used when the specific and generic languages fail to produce a
+ /// match.
+ ///
+ /// Item values are modified according to these rules:
+ ///
+ /// \li If the selected item is from a match with the specific language, the value of that
+ /// item is modified. If the existing value of that item matches the existing value of the
+ /// x-default item, the x-default item is also modified. If the array only has 1 existing item
+ /// (which is not x-default), an x-default item is added with the given value.
+ ///
+ /// \li If the selected item is from a match with the generic language and there are no other
+ /// generic matches, the value of that item is modified. If the existing value of that item
+ /// matches the existing value of the x-default item, the x-default item is also modified. If
+ /// the array only has 1 existing item (which is not x-default), an x-default item is added
+ /// with the given value.
+ ///
+ /// \li If the selected item is from a partial match with the generic language and there are
+ /// other partial matches, a new item is created for the specific language. The x-default item
+ /// is not modified.
+ ///
+ /// \li If the selected item is from the last 2 rules then a new item is created for the
+ /// specific language. If the array only had an x-default item, the x-default item is also
+ /// modified. If the array was empty, items are created for the specific language and
+ /// x-default.
+ ///
+ /// @param schemaNS The namespace URI for the alt-text array; see \c GetProperty().
+ ///
+ /// @param altTextName The name of the alt-text array. Can be a general path expression, must
+ /// not be null or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param genericLang The name of the generic language as an RFC 3066 primary subtag. Can be
+ /// null or the empty string if no generic language is wanted.
+ ///
+ /// @param specificLang The name of the specific language as an RFC 3066 tag, or "x-default".
+ /// Must not be null or the empty string.
+ ///
+ /// @param itemValue The new value for the matching array item, specified as a null-terminated
+ /// UTF-8 string.
+ ///
+ /// @param options Option flags, none currently defined.
+
+ void SetLocalizedText ( XMP_StringPtr schemaNS,
+ XMP_StringPtr altTextName,
+ XMP_StringPtr genericLang,
+ XMP_StringPtr specificLang,
+ XMP_StringPtr itemValue,
+ XMP_OptionBits options = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c SetLocalizedText() modifies the value of a selected item in an alt-text array using
+ /// a string object.
+ ///
+ /// Creates an appropriate array item if necessary, and handles special cases for the x-default
+ /// item.
+ ///
+ /// The array item is selected according to these rules:
+ /// \li Look for an exact match with the specific language.
+ /// \li If a generic language is given, look for a partial match.
+ /// \li Look for an x-default item.
+ /// \li Choose the first item.
+ ///
+ /// A partial match with the generic language is where the start of the item's language matches
+ /// the generic string and the next character is '-'. An exact match is also recognized as a
+ /// degenerate case.
+ ///
+ /// You can pass "x-default" as the specific language. In this case, selection of an \c x-default
+ /// item is an exact match by the first rule, not a selection by the 3rd rule. The last 2 rules
+ /// are fallbacks used when the specific and generic languages fail to produce a match.
+ ///
+ /// Item values are modified according to these rules:
+ ///
+ /// \li If the selected item is from a match with the specific language, the value of that
+ /// item is modified. If the existing value of that item matches the existing value of the
+ /// x-default item, the x-default item is also modified. If the array only has 1 existing item
+ /// (which is not x-default), an x-default item is added with the given value.
+ ///
+ /// \li If the selected item is from a match with the generic language and there are no other
+ /// generic matches, the value of that item is modified. If the existing value of that item
+ /// matches the existing value of the x-default item, the x-default item is also modified. If
+ /// the array only has 1 existing item (which is not x-default), an x-default item is added
+ /// with the given value.
+ ///
+ /// \li If the selected item is from a partial match with the generic language and there are
+ /// other partial matches, a new item is created for the specific language. The x-default item
+ /// is not modified.
+ ///
+ /// \li If the selected item is from the last 2 rules then a new item is created for the
+ /// specific language. If the array only had an x-default item, the x-default item is also
+ /// modified. If the array was empty, items are created for the specific language and
+ /// x-default.
+ ///
+ /// @param schemaNS The namespace URI for the alt-text array; see \c GetProperty().
+ ///
+ /// @param altTextName The name of the alt-text array. Can be a general path expression, must
+ /// not be null or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param genericLang The name of the generic language as an RFC 3066 primary subtag. Can be
+ /// null or the empty string if no generic language is wanted.
+ ///
+ /// @param specificLang The name of the specific language as an RFC 3066 tag, or "x-default".
+ /// Must not be null or the empty string.
+ ///
+ /// @param itemValue The new value for the matching array item, specified as a string object.
+ ///
+ /// @param options Option flags, none currently defined.
+
+ void SetLocalizedText ( XMP_StringPtr schemaNS,
+ XMP_StringPtr altTextName,
+ XMP_StringPtr genericLang,
+ XMP_StringPtr specificLang,
+ const tStringObj & itemValue,
+ XMP_OptionBits options = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c DeleteLocalizedText() deletes specific language alternatives from an alt-text array.
+ ///
+ /// The rules for finding the language value to delete are similar to those for \c #SetLocalizedText().
+ ///
+ /// @param schemaNS The namespace URI for the alt-text array; see \c #GetProperty().
+ ///
+ /// @param altTextName The name of the alt-text array. Can be a general path expression, must
+ /// not be null or the empty string; see \c #GetProperty() for namespace prefix usage.
+ ///
+ /// @param genericLang The name of the generic language as an RFC 3066 primary subtag. Can be
+ /// null or the empty string if no generic language is wanted.
+ ///
+ /// @param specificLang The name of the specific language as an RFC 3066 tag, or "x-default".
+ /// Must not be null or the empty string.
+ ///
+ void
+ DeleteLocalizedText ( XMP_StringPtr schemaNS,
+ XMP_StringPtr altTextName,
+ XMP_StringPtr genericLang,
+ XMP_StringPtr specificLang );
+
+ /// @}
+
+ // =============================================================================================
+ /// \name Creating and reading serialized RDF.
+ /// @{
+ ///
+ /// The metadata contained in an XMP object must be serialized as RDF for storage in an XMP
+ /// packet and output to a file. Similarly, metadata in the form of serialized RDF (such as
+ /// metadata read from a file using \c TXMPFiles) must be parsed into an XMP object for
+ /// manipulation with the XMP Toolkit.
+ ///
+ /// These functions support parsing serialized RDF into an XMP object, and serializing an XMP
+ /// object into RDF. The input for parsing can be any valid Unicode encoding. ISO Latin-1 is
+ /// also recognized, but its use is strongly discouraged. Serialization is always as UTF-8.
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c ParseFromBuffer() parses RDF from a series of input buffers into this XMP object.
+ ///
+ /// Use this to convert metadata from serialized RDF form (as, for example, read from an XMP
+ /// packet embedded in a file) into an XMP object that you can manipulate with the XMP Toolkit.
+ /// If this XMP object is empty and the input buffer contains a complete XMP packet, this is the
+ /// same as creating a new XMP object from that buffer with the constructor.
+ ///
+ /// You can use this function to combine multiple buffers into a single metadata tree. To
+ /// terminate an input loop conveniently, pass the option \c #kXMP_ParseMoreBuffers for all
+ /// real input, then make a final call with a zero length and \c #kXMP_NoOptions. The buffers
+ /// can be any length. The buffer boundaries need not respect XML tokens or even Unicode
+ /// characters.
+ ///
+ /// @param buffer A pointer to a buffer of input. Can be null if \c bufferSize is 0.
+ ///
+ /// @param bufferSize The length of the input buffer in bytes. Zero is a valid value.
+ ///
+ /// @param options An options flag that controls how the parse operation is performed. A logical
+ /// OR of these bit-flag constants:
+ /// \li \c #kXMP_ParseMoreBuffers - This is not the last buffer of input, more calls follow.
+ /// \li \c #kXMP_RequireXMPMeta - The \c x:xmpmeta XML element is required around \c rdf:RDF.
+ ///
+ /// @see \c TXMPFiles::GetXMP()
+
+ void ParseFromBuffer ( XMP_StringPtr buffer,
+ XMP_StringLen bufferSize,
+ XMP_OptionBits options = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c SerializeToBuffer() serializes metadata in this XMP object into a string as RDF.
+ ///
+ /// Use this to prepare metadata for storage as an XMP packet embedded in a file. See \c TXMPFiles::PutXMP().
+ ///
+ /// @param rdfString [out] A string object in which to return the serialized RDF. Must not be null.
+ ///
+ /// @param options An options flag that controls how the serialization operation is performed.
+ /// The specified options must be logically consistent; an exception is thrown if they are not.
+ /// A logical OR of these bit-flag constants:
+ /// \li \c kXMP_OmitPacketWrapper - Do not include an XML packet wrapper. This cannot be
+ /// specified together with \c #kXMP_ReadOnlyPacket, \c #kXMP_IncludeThumbnailPad, or
+ /// \c #kXMP_ExactPacketLength.
+ /// \li \c kXMP_ReadOnlyPacket - Create a read-only XML packet wapper. Cannot be specified
+ /// together with \c kXMP_OmitPacketWrapper.
+ /// \li \c kXMP_UseCompactFormat - Use a highly compact RDF syntax and layout.
+ /// \li \c kXMP_IncludeThumbnailPad - Include typical space for a JPEG thumbnail in the
+ /// padding if no \c xmp:Thumbnails property is present. Cannot be specified together with
+ /// \c kXMP_OmitPacketWrapper.
+ /// \li \c kXMP_ExactPacketLength - The padding parameter provides the overall packet length.
+ /// The actual amount of padding is computed. An exception is thrown if the packet exceeds
+ /// this length with no padding. Cannot be specified together with
+ /// \c kXMP_OmitPacketWrapper.
+ ///
+ /// In addition to the above options, you can include one of the following encoding options:
+ /// \li \c #kXMP_EncodeUTF8 - Encode as UTF-8, the default.
+ /// \li \c #kXMP_EncodeUTF16Big - Encode as big-endian UTF-16.
+ /// \li \c #kXMP_EncodeUTF16Little - Encode as little-endian UTF-16.
+ /// \li \c #kXMP_EncodeUTF32Big - Encode as big-endian UTF-32.
+ /// \li \c #kXMP_EncodeUTF32Little - Encode as little-endian UTF-32.
+ ///
+ /// @param padding The amount of padding to be added if a writeable XML packet is created. If
+ /// zero (the default) an appropriate amount of padding is computed.
+ ///
+ /// @param newline The string to be used as a line terminator. If empty, defaults to linefeed,
+ /// U+000A, the standard XML newline.
+ ///
+ /// @param indent The string to be used for each level of indentation in the serialized RDF. If
+ /// empty, defaults to two ASCII spaces, U+0020.
+ ///
+ /// @param baseIndent The number of levels of indentation to be used for the outermost XML
+ /// element in the serialized RDF. This is convenient when embedding the RDF in other text.
+
+ void SerializeToBuffer ( tStringObj * rdfString,
+ XMP_OptionBits options,
+ XMP_StringLen padding,
+ XMP_StringPtr newline,
+ XMP_StringPtr indent = "",
+ XMP_Index baseIndent = 0 ) const;
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c SerializeToBuffer() serializes metadata in this XMP object into a string as RDF.
+ ///
+ /// This simpler form of the function uses default values for the \c newline, \c indent, and
+ /// \c baseIndent parameters.
+ ///
+ /// @param rdfString [out] A string object in which to return the serialized RDF. Must not be null.
+ ///
+ /// @param options An options flag that controls how the serialization operation is performed.
+ /// The specified options must be logically consistent; an exception is thrown if they are not.
+ /// A logical OR of these bit-flag constants:
+ /// \li \c kXMP_OmitPacketWrapper - Do not include an XML packet wrapper. This cannot be
+ /// specified together with \c #kXMP_ReadOnlyPacket, \c #kXMP_IncludeThumbnailPad, or
+ /// \c #kXMP_ExactPacketLength.
+ /// \li \c kXMP_ReadOnlyPacket - Create a read-only XML packet wapper. Cannot be specified
+ /// together with \c kXMP_OmitPacketWrapper.
+ /// \li \c kXMP_UseCompactFormat - Use a highly compact RDF syntax and layout.
+ /// \li \c kXMP_IncludeThumbnailPad - Include typical space for a JPEG thumbnail in the
+ /// padding if no \c xmp:Thumbnails property is present. Cannot be specified together with
+ /// \c kXMP_OmitPacketWrapper.
+ /// \li \c kXMP_ExactPacketLength - The padding parameter provides the overall packet length.
+ /// The actual amount of padding is computed. An exception is thrown if the packet exceeds
+ /// this length with no padding. Cannot be specified together with
+ /// \c kXMP_OmitPacketWrapper.
+ ///
+ /// In addition to the above options, you can include one of the following encoding options:
+ /// \li \c #kXMP_EncodeUTF8 - Encode as UTF-8, the default.
+ /// \li \c #kXMP_EncodeUTF16Big - Encode as big-endian UTF-16.
+ /// \li \c #kXMP_EncodeUTF16Little - Encode as little-endian UTF-16.
+ /// \li \c #kXMP_EncodeUTF32Big - Encode as big-endian UTF-32.
+ /// \li \c #kXMP_EncodeUTF32Little - Encode as little-endian UTF-32.
+ ///
+ /// @param padding The amount of padding to be added if a writeable XML packet is created.
+ /// If zero (the default) an appropriate amount of padding is computed.
+
+ void SerializeToBuffer ( tStringObj * rdfString,
+ XMP_OptionBits options = 0,
+ XMP_StringLen padding = 0 ) const;
+
+ /// @}
+ // =============================================================================================
+ // Miscellaneous Member Functions
+ // ==============================
+
+ // ---------------------------------------------------------------------------------------------
+ /// \name Helper functions.
+ /// @{
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Retrieves an internal reference that can be safely passed across DLL boundaries and
+ /// reconstructed.
+ ///
+ /// The \c TXMPMeta class is a normal C++ template, it is instantiated and local to each client
+ /// executable, as are the other \c TXMP* classes. Different clients might not use the same
+ /// string type to instantiate \c TXMPMeta.
+ ///
+ /// Because of this you should not pass \c SXMPMeta objects, or pointers to \c SXMPMeta objects,
+ /// across DLL boundaries. Use this function to obtain a safe internal reference that you can
+ /// pass, then construct a local object on the callee side. This construction does not create a
+ /// cloned XMP tree, it is the same underlying XMP object safely wrapped in each client's
+ /// \c SXMPMeta object.
+ ///
+ /// Use this function and the associated constructor like this:
+ /// \li The callee's header contains:
+ /// <pre>
+ /// CalleeMethod ( XMPMetaRef xmpRef );
+ /// </pre>
+ ///
+ /// \li The caller's code contains:
+ /// <pre>
+ /// SXMPMeta callerXMP;
+ /// CalleeMethod ( callerXMP.GetInternalRef() );
+ /// </pre>
+ ///
+ /// \li The callee's code contains:
+ /// <pre>
+ /// SXMPMeta calleeXMP ( xmpRef );
+ /// </pre>
+ ///
+ /// @return The reference object.
+
+ XMPMetaRef GetInternalRef() const;
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c GetObjectName() retrieves the client-assigned name of this XMP object.
+ ///
+ /// Assign this name with \c SetObjectName().
+ ///
+ /// @param name [out] A string object in which to return the name.
+
+ void GetObjectName ( tStringObj * name ) const;
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c SetObjectName() assigns a name to this XMP object.
+ ///
+ /// Retrieve this client-assigned name with \c GetObjectName().
+ ///
+ /// @param name The name as a null-terminated UTF-8 string.
+
+ void SetObjectName ( XMP_StringPtr name );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c SetObjectName() assigns a name to this XMP object.
+ ///
+ /// Retrieve this client-assigned name with \c GetObjectName().
+ ///
+ /// @param name The name as a string object.
+
+ void SetObjectName ( tStringObj name );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c Sort() sorts the data model tree of an XMP object.
+ ///
+ /// Use this function to sort the data model of an XMP object into a canonical order. This can
+ /// be convenient when comparing data models, (e.g. by text comparison of DumpObject output).
+ ///
+ /// At the top level the namespaces are sorted by their prefixes. Within a namespace, the top
+ /// level properties are sorted by name. Within a struct, the fields are sorted by their
+ /// qualified name, i.e. their XML prefix:local form. Unordered arrays of simple items are
+ /// sorted by value. Language Alternative arrays are sorted by the xml:lang qualifiers, with
+ /// the "x-default" item placed first.
+
+ void Sort();
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c Erase() restores the object to a "just constructed" state.
+
+ void Erase();
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c Clone() creates a deep copy of an XMP object.
+ ///
+ /// Use this function to copy an entire XMP metadata tree. Assignment and copy constructors only
+ /// increment a reference count, they do not do a deep copy. This function returns an object,
+ /// not a pointer. The following shows correct usage:
+ ///
+ /// <pre>
+ /// SXMPMeta * clone1 = new SXMPMeta ( sourceXMP.Clone() ); // This works.
+ /// SXMPMeta clone2 ( sourceXMP.Clone ); // This works also. (Not a pointer.)
+ /// </pre>
+ /// The \c clone2 example does not use an explicit pointer.
+ /// This is good for local usage, protecting against memory leaks.
+ ///
+ /// This is an example of incorrect usage:
+ /// <pre>
+ /// SXMPMeta * clone3 = &sourceXMP.Clone(); // ! This does not work!
+ /// </pre>
+ /// The assignment to \c clone3 creates a temporary object, initializes it with the clone,
+ /// assigns the address of the temporary to \c clone3, then deletes the temporary.
+ ///
+ /// @param options Option flags, not currently defined..
+ ///
+ /// @return An XMP object cloned from the original.
+
+ TXMPMeta Clone ( XMP_OptionBits options = 0 ) const;
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c CountArrayItems() reports the number of items currently defined in an array.
+ ///
+ /// @param schemaNS The namespace URI; see \c GetProperty().
+ ///
+ /// @param arrayName The name of the array. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @return The number of items.
+
+ XMP_Index CountArrayItems ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName ) const;
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c DumpObject() outputs the content of an XMP object to a callback handler for debugging.
+ ///
+ /// Invokes a client-defined callback for each line of output.
+ ///
+ /// @param outProc The client-defined procedure to handle each line of output.
+ ///
+ /// @param clientData A pointer to client-defined data to pass to the handler.
+ ///
+ /// @return A success-fail status value, returned from the handler. Zero is success, failure
+ /// values are client-defined.
+ ///
+ /// @see Static function \c DumpNamespaces()
+
+ XMP_Status DumpObject ( XMP_TextOutputProc outProc,
+ void * clientData ) const;
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Not implemented
+ XMP_OptionBits GetObjectOptions() const;
+
+ // ---------------------------------------------------------------------------------------------
+ /// \brief Not implemented
+ void SetObjectOptions ( XMP_OptionBits options );
+
+ /// @}
+
+ // =============================================================================================
+ // Error notifications
+ // ===================
+
+ // ---------------------------------------------------------------------------------------------
+ /// \name Error notifications
+ /// @{
+ ///
+ /// From the beginning through version 5.5, XMP Tookit errors result in throwing an \c XMP_Error
+ /// exception. For the most part exceptions were thrown early and thus API calls aborted as soon
+ /// as an error was detected. Starting in version 5.5, support has been added for notifications
+ /// of errors arising in calls to \c TXMPMeta functions.
+ ///
+ /// A client can register an error notification callback function for a \c TXMPMeta object. This
+ /// can be done as a global default or individually to each object. The global default applies
+ /// to all objects created after it is registered. Within the object there is no difference
+ /// between the global default or explicitly registered callback. The callback function returns
+ /// a \c bool value indicating if recovery should be attempted (true) or an exception thrown
+ /// (false). If no callback is registered, a best effort at recovery and continuation will be
+ /// made with an exception thrown if recovery is not possible.
+ ///
+ /// The number of notifications delivered for a given TXMPMeta object can be limited. This is
+ /// intended to reduce chatter from multiple or cascading errors. The limit is set when the
+ /// callback function is registered. This limits the number of notifications of the highest
+ /// severity delivered or less. If a higher severity error occurs, the counting starts again.
+ /// The limit and counting can be reset at any time, see \c ResetErrorCallbackLimit.
+
+ // --------------------------------------------------------------------------------------------
+ /// @brief SetDefaultErrorCallback() registers a global default error notification callback.
+ ///
+ /// @param proc The client's callback function.
+ ///
+ /// @param context Client-provided context for the callback.
+ ///
+ /// @param limit A limit on the number of notifications to be delivered.
+
+ static void SetDefaultErrorCallback ( XMPMeta_ErrorCallbackProc proc, void* context = 0, XMP_Uns32 limit = 1 );
+
+ // --------------------------------------------------------------------------------------------
+ /// @brief SetErrorCallback() registers an error notification callback.
+ ///
+ /// @param proc The client's callback function.
+ ///
+ /// @param context Client-provided context for the callback.
+ ///
+ /// @param limit A limit on the number of notifications to be delivered.
+
+ void SetErrorCallback ( XMPMeta_ErrorCallbackProc proc, void* context = 0, XMP_Uns32 limit = 1 );
+
+ // --------------------------------------------------------------------------------------------
+ /// @brief ResetErrorCallbackLimit() resets the error notification limit and counting. It has no
+ /// effect if an error notification callback function is not registered.
+ ///
+ /// @param limit A limit on the number of notifications to be delivered.
+
+ void ResetErrorCallbackLimit ( XMP_Uns32 limit = 1 );
+
+ /// @}
+
+ // =============================================================================================
+
+ XMPMetaRef xmpRef; // *** Should be private, see below.
+
+private:
+
+#if 0 // *** VS.Net and gcc seem to not handle the friend declarations properly.
+ friend class TXMPIterator <class tStringObj>;
+ friend class TXMPUtils <class tStringObj>;
+#endif
+
+ static void SetClientString ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen );
+
+}; // class TXMPMeta
+
+#endif // __TXMPMeta_hpp__
diff --git a/gpr/source/lib/xmp_core/public/include/TXMPUtils.hpp b/gpr/source/lib/xmp_core/public/include/TXMPUtils.hpp
new file mode 100644
index 0000000..79ade07
--- /dev/null
+++ b/gpr/source/lib/xmp_core/public/include/TXMPUtils.hpp
@@ -0,0 +1,967 @@
+#ifndef __TXMPUtils_hpp__
+#define __TXMPUtils_hpp__ 1
+
+#if ( ! __XMP_hpp__ )
+ #error "Do not directly include, use XMP.hpp"
+#endif
+
+// =================================================================================================
+// ADOBE SYSTEMS INCORPORATED
+// Copyright 2002 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.
+// =================================================================================================
+
+// =================================================================================================
+/// \file TXMPUtils.hpp
+/// \brief API for access to the XMP Toolkit utility services.
+///
+/// \c TXMPUtils is the template class providing utility services for the XMP Toolkit. It must be
+/// instantiated with a string class such as \c std::string. See the instructions in XMP.hpp, and
+/// the Overview for a discussion of the overall architecture of the XMP API.
+// =================================================================================================
+
+// =================================================================================================
+/// \class TXMPUtils TXMPUtils.hpp
+/// @brief API for access to the XMP Toolkit utility services.
+///
+/// \c TXMPUtils is a template class which must be instantiated with a string class such as
+/// \c std::string. See the instructions in XMP.hpp, and the Overview for a discussion of the overall
+/// architecture of the XMP API.
+///
+/// This class defines helper functions that support the basic metadata manipulation provided by
+/// \c TXMPMeta. All of the functions are static; that is, you call them directly from the concrete
+/// class (\c SXMPUtils), which is never itself instantiated.
+///
+/// General categories of utilities include:
+///
+/// \li Composing complex path expressions, which you can then pass to the property access
+/// functions in \c TXMPMeta
+/// \li Converting between binary and string forms of property values
+/// \li Manipulating date/time values
+/// \li Encoding and decoding base-64 strings
+/// \li JPEG file handling
+/// \li Editing aids for creating a user interface for the XMP Toolkit
+// =================================================================================================
+
+template <class tStringObj> class TXMPUtils {
+
+public:
+
+ // =============================================================================================
+ // No constructors or destructor declared or needed
+ // ================================================
+
+ // ============================================================================================
+ /// \name Path composition
+ /// @{
+ ///
+ /// These functions provide support for composing path expressions to deeply nested properties.
+ /// The functions in \c TXMPMeta such as \c TXMPMeta::GetProperty(),
+ /// \c TXMPMeta::GetArrayItem(), and \c TXMPMeta::GetStructField() provide easy access to top level
+ /// simple properties, items in top level arrays, and fields of top level structs. They are
+ /// not as convenient for more complex things, such as fields several levels deep in a complex
+ /// struct, or fields within an array of structs, or items of an array that is a field of a
+ /// struct. You can use these utility functions to compose these paths, which you can then pass
+ /// to the property access functions. You can also compose paths to top-level array items or
+ /// struct fields so that you can use the binary accessors such as
+ /// \c TXMPMeta::GetProperty_Int().
+ ///
+ /// You can use these functions is to compose a complete path expression, or all but the last
+ /// component. For example, suppose you have a property that is an array of integers within a
+ /// struct. You can access one of the array items like this:
+ ///
+ /// <pre>
+ /// SXMPUtils::ComposeStructFieldPath ( schemaNS, "Struct", fieldNS, "Array", &path );
+ /// SXMPUtils::ComposeArrayItemPath ( schemaNS, path, index, &path );
+ /// exists = xmpObj.GetProperty_Int ( schemaNS, path, &value, &options );
+ /// </pre>
+ ///
+ /// You could also use this code if you want the string form of the integer:
+ ///
+ /// <pre>
+ /// SXMPUtils::ComposeStructFieldPath ( schemaNS, "Struct", fieldNS, "Array", &path );
+ /// xmpObj.GetArrayItem ( schemaNS, path, index, &value, &options );
+ /// </pre>
+ ///
+ /// \note It might look confusing that the \c schemaNS is passed in all of the calls above. This
+ /// is because the XMP Toolkit keeps the top-level "schema" namespace separate from the rest of
+ /// the path expression.
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c ComposeArrayItemPath() composes the path expression for an item in an array.
+ ///
+ /// The returned string is in the form <tt>ns:arrayName[i]</tt>, where "ns" is the prefix for
+ /// the specified namespace, and "i" is the decimal representation of specified item index.
+ /// If the last item was specified, the path is <tt>ns:arrayName[last()]</tt>.
+ ///
+ /// @param schemaNS The namespace URI for the array; see \c GetProperty().
+ ///
+ /// @param arrayName The name of the array. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param itemIndex The 1-based index of the desired item. Use the macro
+ /// \c #kXMP_ArrayLastItem to specify the last existing array item.
+ ///
+ /// @param fullPath [out] A string in which to return the composed path.
+
+ static void ComposeArrayItemPath ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex,
+ tStringObj * fullPath );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c ComposeStructFieldPath() composes the path expression for a field in a struct.
+ ///
+ /// The returned string is in the form <tt>ns:structName/fNS:fieldName</tt>, where "ns" is the
+ /// prefix for the schema namespace, and "fNS" is the prefix for field namespace.
+ ///
+ /// @param schemaNS The namespace URI for the struct; see \c GetProperty().
+ ///
+ /// @param structName The name of the struct. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param fieldNS The namespace URI for the field. Same URI and prefix usage as the
+ /// \c schemaNS and \c structName parameters.
+ ///
+ /// @param fieldName The name of the field. Must be a single XML name, must not be null or the
+ /// empty string. Same URI and prefix usage as the \c schemaNS and \c structName parameters.
+ ///
+ /// @param fullPath [out] A string in which to return the composed path.
+
+ static void ComposeStructFieldPath ( XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ tStringObj * fullPath );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c ComposeQualifierPath() composes the path expression for a qualifier.
+ ///
+ /// The returned string is in the form <tt>ns:propName/?qNS:qualName</tt>, where "ns" is the
+ /// prefix for the schema namespace, and "qNS" is the prefix for the qualifier namespace.
+ ///
+ /// @param schemaNS The namespace URI; see \c GetProperty().
+ ///
+ /// @param propName The name of the property to which the qualifier is attached. Can be a
+ /// general path expression, must not be null or the empty string; see \c GetProperty() for
+ /// namespace prefix usage.
+ ///
+ /// @param qualNS The namespace URI for the qualifier. Same URI and prefix usage as the
+ /// \c schemaNS and \c propName parameters.
+ ///
+ /// @param qualName The name of the qualifier. Must be a single XML name, must not be null or the
+ /// empty string. Same URI and prefix usage as the \c schemaNS and \c propName parameters.
+ ///
+ /// @param fullPath [out] A string in which to return the composed path.
+
+ static void ComposeQualifierPath ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName,
+ tStringObj * fullPath );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c ComposeLangSelector() composes the path expression to select an alternate item by language.
+ ///
+ /// Path syntax allows two forms of "content addressing" to select an item in an array of
+ /// alternatives. The form used in this function lets you select an item in an alt-text array
+ /// based on the value of its \c xml:lang qualifier. The other form of content addressing is
+ /// shown in \c ComposeFieldSelector().
+ ///
+ /// The returned string is in the form <tt>ns:arrayName[\@xml:lang='langName']</tt>, where
+ /// "ns" is the prefix for the schema namespace
+ ///
+ /// This function provides a path expression that is explicitly and only for a specific
+ /// language. In most cases, \c TXMPMeta::SetLocalizedText() and \c TXMPMeta::GetLocalizedText()
+ /// are preferred, because they provide extra logic to choose the appropriate language and
+ /// maintain consistency with the 'x-default' value.
+ ///
+ /// @param schemaNS The namespace URI for the array; see \c GetProperty().
+ ///
+ /// @param arrayName The name of the array. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param langName The RFC 3066 code for the desired language, as a null-terminated UTF-8 string.
+ ///
+ /// @param fullPath [out] A string in which to return the composed path.
+
+ static void ComposeLangSelector ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr langName,
+ tStringObj * fullPath );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c ComposeLangSelector() composes a path expression to select an alternate item by language.
+ ///
+ /// Path syntax allows two forms of "content addressing" to select an item in an array of
+ /// alternatives. The form used in this function lets you select an item in an alt-text array
+ /// based on the value of its \c xml:lang qualifier. The other form of content addressing is
+ /// shown in \c ComposeFieldSelector().
+ ///
+ /// The returned string is in the form <tt>ns:arrayName[\@xml:lang='langName']</tt>, where
+ /// "ns" is the prefix for the schema namespace
+ ///
+ /// This function provides a path expression that is explicitly and only for a specific
+ /// language. In most cases, \c TXMPMeta::SetLocalizedText() and \c TXMPMeta::GetLocalizedText()
+ /// are preferred, because they provide extra logic to choose the appropriate language and
+ /// maintain consistency with the 'x-default' value.
+ ///
+ /// @param schemaNS The namespace URI for the array; see \c GetProperty().
+ ///
+ /// @param arrayName The name of the array. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param langName The RFC 3066 code for the desired language, as a string object.
+ ///
+ /// @param fullPath [out] A string in which to return the composed path.
+
+ static void ComposeLangSelector ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ const tStringObj & langName,
+ tStringObj * fullPath );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c ComposeFieldSelector() composes a path expression to select an alternate item by a field's value.
+ ///
+ /// Path syntax allows two forms of "content addressing" to select an item in an array of
+ /// alternatives. The form used in this function lets you select an item in an array of structs
+ /// based on the value of one of the fields in the structs. The other form of content addressing
+ /// is shown in \c ComposeLangSelector().
+ ///
+ /// For example, consider a simple struct that has two fields, the name of a city and the URI of
+ /// an FTP site in that city. Use this to create an array of download alternatives. You can show
+ /// the user a popup built from the values of the city fields, then get the corresponding URI as
+ /// follows:
+ /// <pre>
+ /// ComposeFieldSelector ( schemaNS, "Downloads", fieldNS, "City", chosenCity, &path );
+ /// exists = GetStructField ( schemaNS, path, fieldNS, "URI", &uri );
+ /// </pre>
+ ///
+ /// The returned string is in the form <tt>ns:arrayName[fNS:fieldName='fieldValue']</tt>, where
+ /// "ns" is the prefix for the schema namespace and "fNS" is the prefix for the field namespace.
+ ///
+ /// @param schemaNS The namespace URI for the array; see \c GetProperty().
+ ///
+ /// @param arrayName The name of the array. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param fieldNS The namespace URI for the field used as the selector. Same URI and prefix
+ /// usage as the \c schemaNS and \c arrayName parameters.
+ ///
+ /// @param fieldName The name of the field used as the selector. Must be a single XML name, must
+ /// not be null or the empty string. It must be the name of a field that is itself simple.
+ ///
+ /// @param fieldValue The desired value of the field, specified as a null-terminated UTF-8 string.
+ ///
+ /// @param fullPath [out] A string in which to return the composed path.
+
+ static void ComposeFieldSelector ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ XMP_StringPtr fieldValue,
+ tStringObj * fullPath );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c ComposeFieldSelector() composes a path expression to select an alternate item by a field's value.
+ ///
+ /// Path syntax allows two forms of "content addressing" to select an item in an array of
+ /// alternatives. The form used in this function lets you select an item in an array of structs
+ /// based on the value of one of the fields in the structs. The other form of content addressing
+ /// is shown in \c ComposeLangSelector().
+ ///
+ /// For example, consider a simple struct that has two fields, the name of a city and the URI of
+ /// an FTP site in that city. Use this to create an array of download alternatives. You can show
+ /// the user a popup built from the values of the city fields, then get the corresponding URI as
+ /// follows:
+ /// <pre>
+ /// ComposeFieldSelector ( schemaNS, "Downloads", fieldNS, "City", chosenCity, &path );
+ /// exists = GetStructField ( schemaNS, path, fieldNS, "URI", &uri );
+ /// </pre>
+ ///
+ /// The returned string is in the form <tt>ns:arrayName[fNS:fieldName='fieldValue']</tt>, where
+ /// "ns" is the prefix for the schema namespace and "fNS" is the prefix for the field namespace.
+ ///
+ /// @param schemaNS The namespace URI for the array; see \c GetProperty().
+ ///
+ /// @param arrayName The name of the array. Can be a general path expression, must not be null
+ /// or the empty string; see \c GetProperty() for namespace prefix usage.
+ ///
+ /// @param fieldNS The namespace URI for the field used as the selector. Same URI and prefix
+ /// usage as the \c schemaNS and \c arrayName parameters.
+ ///
+ /// @param fieldName The name of the field used as the selector. Must be a single XML name, must
+ /// not be null or the empty string. It must be the name of a field that is itself simple.
+ ///
+ /// @param fieldValue The desired value of the field, specified as a string object.
+ ///
+ /// @param fullPath [out] A string in which to return the composed path.
+
+ static void ComposeFieldSelector ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ const tStringObj & fieldValue,
+ tStringObj * fullPath );
+
+ /// @}
+
+ // =============================================================================================
+ /// \name Conversion between binary types and strings
+ /// @{
+ ///
+ /// The main accessors in \c TXMPMeta set and retrieve property values as strings. additional
+ /// functions, such as \c TXMPMeta::SetPropertyInt(), set and retrieve property values as
+ /// explicit binary data types. Use these functions to convert between binary and string
+ /// values.
+ ///
+ /// Strings can be specified as null-terminated UTF-8 (\c #XMP_StringPtr), or as string
+ /// objects (\c tStringObj) of the type declared when instantiating the XMP classes; see
+ /// \c XMP.hpp. Alternate forms of each conversion function allow either type of string.
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c ConvertFromBool() converts a Boolean value to a string.
+ ///
+ /// The string values of Booleans are returned by the macros \c #kXMP_TrueStr and
+ /// \c #kXMP_FalseStr in \c XMP_Const.h.
+ ///
+ /// @param binValue The Boolean value to be converted.
+ ///
+ /// @param strValue [out] A buffer in which to return the string representation of the value.
+
+ static void ConvertFromBool ( bool binValue,
+ tStringObj * strValue );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c ConvertFromInt() converts a 32-bit integer value to a string.
+ ///
+ /// @param binValue The integer value to be converted.
+ ///
+ /// @param format Optional. A C \c sprintf format for the conversion. Default is "%d".
+ ///
+ /// @param strValue [out] A buffer in which to return the string representation of the value.
+
+ static void ConvertFromInt ( long binValue,
+ XMP_StringPtr format,
+ tStringObj * strValue );
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c ConvertFromInt64() converts a 64-bit integer value to a string.
+ ///
+ /// @param binValue The integer value to be converted.
+ ///
+ /// @param format Optional. A C \c sprintf format for the conversion. Default is "%d".
+ ///
+ /// @param strValue [out] A buffer in which to return the string representation of the value.
+
+ static void ConvertFromInt64 ( long long binValue,
+ XMP_StringPtr format,
+ tStringObj * strValue );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c ConvertFromFloat() converts a floating-point value to a string.
+ ///
+ /// @param binValue The floating-point value to be converted.
+ ///
+ /// @param format Optional. A C \c sprintf format for the conversion. Default is "%d".
+ ///
+ /// @param strValue [out] A buffer in which to return the string representation of the value.
+
+ static void ConvertFromFloat ( double binValue,
+ XMP_StringPtr format,
+ tStringObj * strValue );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c ConvertFromDate() converts a date/time value to a string.
+ ///
+ /// Formats a date according to the ISO 8601 profile in http://www.w3.org/TR/NOTE-datetime:
+ /// <pre>
+ /// YYYY
+ /// YYYY-MM
+ /// YYYY-MM-DD
+ /// YYYY-MM-DDThh:mmTZD
+ /// YYYY-MM-DDThh:mm:ssTZD
+ /// YYYY-MM-DDThh:mm:ss.sTZD
+ /// </pre>
+ ///
+ /// \c YYYY = four-digit year, formatted as "%.4d" <br>
+ /// \c MM = two-digit month (01=January) <br>
+ /// \c DD = two-digit day of month (01 through 31) <br>
+ /// \c hh = two digits of hour (00 through 23) <br>
+ /// \c mm = two digits of minute (00 through 59) <br>
+ /// \c ss = two digits of second (00 through 59) <br>
+ /// \c s = one or more digits representing a decimal fraction of a second <br>
+ /// \c TZD = time zone designator (Z or +hh:mm or -hh:mm)
+ ///
+ /// Time-only input is allowed where the year, month, and day are all zero. This is output as
+ /// "0000-00-00...".
+ ///
+ /// @note ISO 8601 does not allow years less than 1000 or greater than 9999. This API allows
+ /// any year, even negative ones. The W3C profile also requires a time zone designator if a time
+ /// is present, this API treats the time zone designator as optional. The XMP_DateTime type has
+ /// an explicit notion of zone-less time.
+ ///
+ /// @param binValue The date/time value to be converted.
+ ///
+ /// @param strValue [out] A buffer in which to return the ISO 8601 string representation of the date/time.
+
+ static void ConvertFromDate ( const XMP_DateTime & binValue,
+ tStringObj * strValue );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c ConvertToBool() converts a string to a Boolean value.
+ ///
+ /// The preferred strings are those returned by the macros \c #kXMP_TrueStr and \c #kXMP_FalseStr.
+ /// If these do not match, the function does a case insensitive comparison, then simply 't' or 'f',
+ /// and finally non-zero and zero integer representations.
+ ///
+ /// @param strValue The string representation of the value, specified as a null-terminated UTF-8 string.
+ ///
+ /// @return The appropriate C++ bool value for the string.
+
+ static bool ConvertToBool ( XMP_StringPtr strValue );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c ConvertToBool() converts a string to a Boolean value.
+ ///
+ /// Overloads the basic form of the function, allowing you to pass a string object,
+ /// rather than a <tt>const * char</tt>. It is otherwise identical; see details in the canonical form.
+ ///
+ /// @param strValue The string representation of the value, specified as a string object.
+ ///
+ /// @return The appropriate C++ bool value for the string.
+
+ static bool ConvertToBool ( const tStringObj & strValue );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c ConvertToInt() converts a string to a 32-bit integer value.
+ ///
+ /// @param strValue The string representation of the value, specified as a null-terminated UTF-8 string.
+ ///
+ /// @return The 32-bit integer value.
+
+ static long ConvertToInt ( XMP_StringPtr strValue );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c ConvertToInt() converts a string to a 32-bit integer value.
+ ///
+ /// Overloads the basic form of the function, allowing you to pass a string object,
+ /// rather than a <tt>const * char</tt>. It is otherwise identical.
+ ///
+ /// @param strValue The string representation of the value, specified as a string object.
+ ///
+ /// @return The 32-bit integer value.
+
+ static long ConvertToInt ( const tStringObj & strValue );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c ConvertToInt64() converts a string to a 64-bit integer value.
+ ///
+ /// @param strValue The string representation of the value, specified as a null-terminated UTF-8 string.
+ ///
+ /// @return The 64-bit integer value.
+
+ static long long ConvertToInt64 ( XMP_StringPtr strValue );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c ConvertToInt64() converts a string to a 64-bit integer value.
+ ///
+ /// Overloads the basic form of the function, allowing you to pass a string object,
+ /// rather than a <tt>const * char</tt>. It is otherwise identical.
+ ///
+ /// @param strValue The string representation of the value, specified as a string object.
+ ///
+ /// @return The 64-bit integer value.
+
+ static long long ConvertToInt64 ( const tStringObj & strValue );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c ConvertToFloat() converts a string to a floating-point value.
+ ///
+ /// @param strValue The string representation of the value, specified as a null-terminated UTF-8 string.
+ ///
+ /// @return The floating-point value.
+
+ static double ConvertToFloat ( XMP_StringPtr strValue );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c ConvertToFloat() converts a string to a floating-point value.
+ ///
+ /// Overloads the basic form of the function, allowing you to pass a string object,
+ /// rather than a <tt>const * char</tt>. It is otherwise identical.
+ ///
+ /// @param strValue The string representation of the value, specified as a string object.
+ ///
+ /// @return The floating-point value.
+
+ static double ConvertToFloat ( const tStringObj & strValue );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c ConvertToDate() converts a string to a date/time value.
+ ///
+ /// Parses a date according to the ISO 8601 profile in http://www.w3.org/TR/NOTE-datetime:
+ /// <pre>
+ /// YYYY
+ /// YYYY-MM
+ /// YYYY-MM-DD
+ /// YYYY-MM-DDThh:mmTZD
+ /// YYYY-MM-DDThh:mm:ssTZD
+ /// YYYY-MM-DDThh:mm:ss.sTZD
+ /// </pre>
+ ///
+ /// \c YYYY = four-digit year, formatted as "%.4d" <br>
+ /// \c MM = two-digit month (01=January) <br>
+ /// \c DD = two-digit day of month (01 through 31) <br>
+ /// \c hh = two digits of hour (00 through 23) <br>
+ /// \c mm = two digits of minute (00 through 59) <br>
+ /// \c ss = two digits of second (00 through 59) <br>
+ /// \c s = one or more digits representing a decimal fraction of a second <br>
+ /// \c TZD = time zone designator (Z or +hh:mm or -hh:mm)
+ ///
+ /// A missing date portion or missing TZD are tolerated. A missing date value can begin with
+ /// "Thh:" or "hh:"; the year, month, and day are all set to zero in the \c #XMP_DateTime value.
+ /// A missing TZD is assumed to be UTC.
+ ///
+ /// @note ISO 8601 does not allow years less than 1000 or greater than 9999. This API allows
+ /// any year, even negative ones. The W3C profile also requires a time zone designator if a time
+ /// is present, this API treats the time zone designator as optional. The XMP_DateTime type has
+ /// an explicit notion of zone-less time.
+ ///
+ /// @param strValue The ISO 8601 string representation of the date/time, specified as a
+ /// null-terminated UTF-8 string.
+ ///
+ /// @param binValue [out] A buffer in which to return the binary date/time value.
+
+ static void ConvertToDate ( XMP_StringPtr strValue,
+ XMP_DateTime * binValue );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c ConvertToDate() converts a string to a date/time value.
+ ///
+ /// Overloads the basic form of the function, allowing you to pass a string object,
+ /// rather than a <tt>const * char</tt>. It is otherwise identical.
+ /// See details for the canonical form.
+ ///
+ ///
+ /// @param strValue The ISO 8601 string representation of the date/time, specified as a string
+ /// object.
+ ///
+ /// @param binValue [out] A buffer in which to return the binary date/time value.
+
+ static void ConvertToDate ( const tStringObj & strValue,
+ XMP_DateTime * binValue );
+
+ /// @}
+
+ // =============================================================================================
+ /// \name Date-time manipulation
+ /// @{
+ ///
+ /// In addition to the type-conversion functions that convert between strings and binary
+ /// date-time values, these functions create, manipulate, and compare date-time values.
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c CurrentDateTime() obtains the current date and time.
+ ///
+ /// Creates and returns a binary \c #XMP_DateTime value. The returned time is UTC, properly
+ /// adjusted for the local time zone. The resolution of the time is not guaranteed to be finer
+ /// than seconds.
+ ///
+ /// @param time [out] A buffer in which to return the date/time value.
+
+ static void CurrentDateTime ( XMP_DateTime * time );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c SetTimeZone() sets the time zone in a date/time value to the local time zone.
+ ///
+ /// Any existing time zone value is replaced. The other date/time fields are not adjusted in any way.
+ ///
+ /// @param time A pointer to the date-time value, which is modified in place.
+
+ static void SetTimeZone ( XMP_DateTime * time );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c ConvertToUTCTime() ensures that a time is UTC.
+ ///
+ /// If the time zone is not UTC, the time is adjusted and the time zone set to be UTC. The value
+ /// is not modified if the time zone is already UTC or if the value has no time zone.
+ ///
+ /// @param time A pointer to the date-time value, which is modified in place.
+
+ static void ConvertToUTCTime ( XMP_DateTime * time );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c ConvertToLocalTime() ensures that a time is local.
+ ///
+ /// If the time zone is not the local zone, the time is adjusted and the time zone set to be local.
+ /// The value is not modified if the time zone is already the local zone or if the value has no
+ /// time zone.
+ ///
+ /// @param time A pointer to the date-time value, which is modified in place.
+
+ static void ConvertToLocalTime ( XMP_DateTime * time );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c CompareDateTime() compares the order of two date/time values.
+ ///
+ /// Both values are treated as in the same time zone if either has no time zone.
+ ///
+ /// @param left The left-side date/time value.
+ ///
+ /// @param right The right-side date/time value.
+ ///
+ /// @return An integer indicating the order:
+ /// \li -1 if left is earlier than right
+ /// \li 0 if left matches right
+ /// \li +1 if left is later than right
+
+ static int CompareDateTime ( const XMP_DateTime & left,
+ const XMP_DateTime & right );
+
+ /// @}
+
+ // =============================================================================================
+ /// \name Base64 encoding and decoding
+ /// @{
+ ///
+ /// These functions convert between raw data values and Base64-encoded strings.
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c EncodeToBase64() converts a raw data value to a Base64-encoded string.
+ ///
+ /// @param rawStr An \c #XMP_StringPtr (char *) string containing the raw data to be converted.
+ ///
+ /// @param rawLen The number of characters of raw data to be converted.
+ ///
+ /// @param encodedStr [out] A string object in which to return the encoded string.
+
+ static void EncodeToBase64 ( XMP_StringPtr rawStr,
+ XMP_StringLen rawLen,
+ tStringObj * encodedStr );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c EncodeToBase64() converts a raw data value passed in a string object to a Base64-encoded string.
+ ///
+ /// Overloads the basic form of the function, allowing you to pass a string object as input.
+ /// It is otherwise identical.
+ ///
+ /// @param rawStr A string object containing the raw data to be converted.
+ ///
+ /// @param encodedStr [out] A string object in which to return the encoded string.
+
+ static void EncodeToBase64 ( const tStringObj & rawStr,
+ tStringObj * encodedStr );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c DecodeFromBase64() Decodes a Base64-encoded string to raw data.
+ ///
+ /// @param encodedStr An \c #XMP_StringPtr (char *) string containing the encoded data to be converted.
+ ///
+ /// @param encodedLen The number of characters of raw data to be converted.
+ ///
+ /// @param rawStr [out] A string object in which to return the decoded data.
+
+ static void DecodeFromBase64 ( XMP_StringPtr encodedStr,
+ XMP_StringLen encodedLen,
+ tStringObj * rawStr );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c DecodeFromBase64() Decodes a Base64-encoded string, passed as a string object, to raw data.
+ ///
+ /// Overloads the basic form of the function, allowing you to pass a string object as input.
+ /// It is otherwise identical.
+ ///
+ /// @param encodedStr An string object containing the encoded data to be converted.
+ ///
+ /// @param rawStr [out] A string object in which to return the decoded data.
+
+ static void DecodeFromBase64 ( const tStringObj & encodedStr,
+ tStringObj * rawStr );
+
+ /// @}
+
+ // =============================================================================================
+ // =============================================================================================
+ /// \name JPEG file handling
+ /// @{
+ ///
+ /// These functions support the partitioning of XMP in JPEG files into standard and extended
+ /// portions in order to work around the 64KB size limit of JPEG marker segments.
+ ///
+ /// @note (Doc note) Add detail about how to write out and read back extended data
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c PackageForJPEG() creates XMP serializations appropriate for a JPEG file.
+ ///
+ /// The standard XMP in a JPEG file is limited to 64K bytes. This function serializes the XMP
+ /// metadata in an XMP object into a string of RDF (see \c TXMPMeta::SerializeToBuffer()). If
+ /// the data does not fit into the 64K byte limit, it creates a second packet string with the
+ /// extended data.
+ ///
+ /// @param xmpObj The XMP object containing the metadata.
+ ///
+ /// @param standardXMP [out] A string object in which to return the full standard XMP packet.
+ ///
+ /// @param extendedXMP [out] A string object in which to return the serialized extended XMP,
+ /// empty if not needed.
+ ///
+ /// @param extendedDigest [out] A string object in which to return an MD5 digest of the serialized
+ /// extended XMP, empty if not needed.
+ ///
+ /// @see \c MergeFromJPEG()
+
+ static void PackageForJPEG ( const TXMPMeta<tStringObj> & xmpObj,
+ tStringObj * standardXMP,
+ tStringObj * extendedXMP,
+ tStringObj * extendedDigest );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c MergeFromJPEG() merges standard and extended XMP retrieved from a JPEG file.
+ ///
+ /// When an extended partition stores properties that do not fit into the JPEG file limitation
+ /// of 64K bytes, this function integrates those properties back into the same XMP object with
+ /// those from the standard XMP packet.
+ ///
+ /// @param fullXMP [in, out] An XMP object which the caller has initialized from the standard
+ /// XMP packet in a JPEG file. The extended XMP is added to this object.
+ ///
+ /// @param extendedXMP An XMP object which the caller has initialized from the extended XMP
+ /// packet in a JPEG file.
+ ///
+ /// @see \c PackageForJPEG()
+
+ static void MergeFromJPEG ( TXMPMeta<tStringObj> * fullXMP,
+ const TXMPMeta<tStringObj> & extendedXMP );
+
+ /// @}
+
+ // =============================================================================================
+ /// \name Editing utilities
+ /// @{
+ ///
+ /// These functions are useful in implementing a user interface for editing XMP. They
+ /// convert sets of property values to and from displayable and manipulable strings, and perform
+ /// operations on sets of metadata, such as those available from the File Info dialog box.
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c CatenateArrayItems() creates a single edit string from a set of array item values.
+ ///
+ /// Collects the values of all items in an array into a single string, using a specified
+ /// separation string. Each item in the specified array must be a simple string value.
+ ///
+ /// @param xmpObj The XMP object containing the array to be catenated.
+ ///
+ /// @param schemaNS The schema namespace URI for the array. Must not be null or the empty string.
+ ///
+ /// @param arrayName The name of the array. May be a general path expression, must not be null
+ /// or the empty string.
+ ///
+ /// @param separator The string with which to separate the items in the catenated string.
+ /// Defaults to "; ", ASCII semicolon and space (U+003B, U+0020).
+ ///
+ /// @param quotes The character or characters to use as quotes around array items that contain a
+ /// separator. Defaults to the double-quote character ("), ASCII quote (U+0022).
+ ///
+ /// @param options Option flags to control the catenation. <<what options?>>
+ ///
+ /// @param catedStr [out] A string object in which to return the catenated array items.
+ ///
+ /// @see \c SeparateArrayItems()
+
+ static void CatenateArrayItems ( const TXMPMeta<tStringObj> & xmpObj,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr separator,
+ XMP_StringPtr quotes,
+ XMP_OptionBits options,
+ tStringObj * catedStr );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c SeparateArrayItems() updates an array from a concatenated edit string of values.
+ ///
+ /// This reverses the action of \c CatenateArrayItems(), separating out individual array items
+ /// from the edit string and updating the array with the new values. Each item in the array must
+ /// be a simple string value.
+ ///
+ /// @param xmpObj The XMP object containing the array to be updated.
+ ///
+ /// @param schemaNS The schema namespace URI for the array. Must not be null or the empty string.
+ ///
+ /// @param arrayName The name of the array. May be a general path expression, must not be null
+ /// or the empty string.
+ ///
+ /// @param options Option flags to control the separation. <<what options?>>
+ ///
+ /// @param catedStr The concatenated array items, as created by \c CatenateArrayItems(),
+ /// specified as a null-terminated UTF-8 string.
+
+ static void SeparateArrayItems ( TXMPMeta<tStringObj> * xmpObj,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_OptionBits options,
+ XMP_StringPtr catedStr );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c SeparateArrayItems() updates an array from a concatenated edit string of values.
+ ///
+ /// Overloads the basic form of the function, allowing you to pass a string object in which
+ /// to return the concatenated string. It is otherwise identical; see details for the canonical form.
+ ///
+
+ static void SeparateArrayItems ( TXMPMeta<tStringObj> * xmpObj,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_OptionBits options,
+ const tStringObj & catedStr );
+
+ /// @brief \c ApplyTemplate() modifies a working XMP object according to a template object.
+ ///
+ /// The XMP template can be used to add, replace or delete properties from the working XMP object.
+ /// This function replaces the previous \c AppendProperties() function, which is no longer available.
+ /// The actions that you specify determine how the template is applied. Each action can be applied
+ /// individually or combined; if you do not specify any actions, the properties and values in the working
+ /// XMP object do not change.
+ ///
+ /// These actions are available:
+ /// \li Clear (\c #kXMPTemplate_ClearUnnamedProperties): Deletes top-level properties.
+ /// Any top-level property that is present in the template (even with empty value)
+ /// is retained. All other top-level properties in the working object are deleted.
+ ///
+ /// \li Add (\c #kXMPTemplate_AddNewProperties): Adds new properties to the working object if the
+ /// template properties have values. See additional detail below.
+ ///
+ /// \li Replace (\c #kXMPTemplate_ReplaceExistingProperties): Replaces the values of existing top-level
+ /// properties in the working XMP if the value forms match those in the template. Properties
+ /// with empty values in the template are ignored. If combined with Clear or Add actions,
+ /// those take precedence; values are cleared or added, rather than replaced.
+ ///
+ /// \li Replace/Delete empty (\c #kXMPTemplate_ReplaceWithDeleteEmpty): Replaces values in the same way
+ /// as the simple Replace action, and also deletes properties if the value in the template is empty.
+ /// If combined with Clear or Add actions, those take precedence; values are cleared or added,
+ /// rather than replaced.
+ ///
+ /// \li Include internal (\c #kXMPTemplate_IncludeInternalProperties): Performs specified action
+ /// on internal properties as well as external properties. By default, internal properties
+ /// are ignored for all actions.
+ ///
+ /// The Add behavior depends on the type of property:
+ /// <ul>
+ /// <li> If a top-level property is not in the working XMP, and has a value in the template,
+ /// the property and value are added. Empty properties are not added. </li>
+ /// <li> If a property is in both the working XMP and template, the value forms must match, otherwise
+ /// the template is ignored for that property.</li>
+ /// <li> If a struct is present in both the working XMP and template, the individual fields of the
+ /// template struct are added as appropriate; that is, the logic is recursively applied to the fields.
+ /// Struct values are equivalent if they have the same fields with equivalent values. </li>
+ /// <li> If an array is present in both the working XMP and template, items from the template are
+ /// added if the value forms match. Array values match if they have sets of equivalent items,
+ /// regardless of order.</li>
+ /// <li> Alt-text arrays use the \c xml:lang qualifier as a key, adding languages that are missing. </li>
+ /// </ul>
+ /// Array item checking is n-squared; this can be time-intensive if the Replace option is
+ /// not specified. Each source item is checked to see if it already exists in the destination,
+ /// without regard to order or duplicates. Simple items are compared by value and \c xml:lang
+ /// qualifier; other qualifiers are ignored. Structs are recursively compared by field names,
+ /// without regard to field order. Arrays are compared by recursively comparing all items.
+
+ /// @param workingXMP The destination XMP object.
+ ///
+ /// @param templateXMP The template to apply to the destination XMP object.
+ ///
+ /// @param actions Option flags to control the copying. If none are specified, the properties and values
+ /// in the working XMP do not change. A logical OR of these bit-flag constants:
+ /// \li \c #kXMPTemplate_ClearUnnamedProperties -- Delete anything that is not in the template
+ /// \li \c #kXMPTemplate_AddNewProperties -- Add properties; see detailed description.
+ /// \li \c #kXMPTemplate_ReplaceExistingProperties -- Replace the values of existing properties.
+ /// \li \c #kXMPTemplate_ReplaceWithDeleteEmpty -- Replace the values of existing properties
+ /// and delete properties if the new value is empty.
+ /// \li \c #kXMPTemplate_IncludeInternalProperties -- Operate on internal properties as well as
+ /// external properties.
+ ///
+
+ static void ApplyTemplate ( TXMPMeta<tStringObj> * workingXMP,
+ const TXMPMeta<tStringObj> & templateXMP,
+ XMP_OptionBits actions );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c RemoveProperties() removes multiple properties from an XMP object.
+ ///
+ /// The operation depends on how the namespace and property are specified:
+ ///
+ /// \li Non-empty \c schemaNS and \c propName - The named property is removed if it is an
+ /// external property, or if the \c #kXMPUtil_DoAllProperties option flag is set. It does not
+ /// matter whether the named property is an actual property or an alias.
+ ///
+ /// \li Non-empty \c schemaNS and empty \c propName - All external properties in the named
+ /// schema are removed. Internal properties are also removed if the
+ /// \c #kXMPUtil_DoAllProperties option flag is set. In addition, aliases from the named schema
+ /// are removed if the \c #kXMPUtil_IncludeAliases option flag is set.
+ ///
+ /// \li Empty \c schemaNS and empty \c propName - All external properties in all schemas are
+ /// removed. Internal properties are also removed if the \c #kXMPUtil_DoAllProperties option
+ /// flag is set. Aliases are handled implicitly, because the associated actuals are removed or
+ /// not.
+ ///
+ /// \li It is an error to pass an empty \c schemaNS and non-empty \c propName.
+ ///
+ /// @param xmpObj The XMP object containing the properties to be removed.
+ ///
+ /// @param schemaNS Optional schema namespace URI for the properties to be removed.
+ ///
+ /// @param propName Optional path expression for the property to be removed.
+ ///
+ /// @param options Option flags to control the deletion operation. A logical OR of these
+ /// bit-flag constants:
+ /// \li \c #kXMPUtil_DoAllProperties - Delete internal properties in addition to external properties.
+ /// \li \c #kXMPUtil_IncludeAliases - Include aliases if the schema is explicitly specified.
+
+ static void RemoveProperties ( TXMPMeta<tStringObj> * xmpObj,
+ XMP_StringPtr schemaNS = 0,
+ XMP_StringPtr propName = 0,
+ XMP_OptionBits options = 0 );
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief \c DuplicateSubtree() replicates a subtree from one XMP object into another.
+ ///
+ /// The destination can be a different namespace and root location in the same object, or the
+ /// same or a different location in another XMP object.
+ ///
+ /// @param source The source XMP object.
+ ///
+ /// @param dest The destination XMP object.
+ ///
+ /// @param sourceNS The schema namespace URI for the source subtree.
+ ///
+ /// @param sourceRoot The root location for the source subtree. Can be a general path expression,
+ /// must not be null or the empty string.
+ ///
+ /// @param destNS The schema namespace URI for the destination. Defaults to the source namespace.
+ ///
+ /// @param destRoot The root location for the destination. Can be a general path expression.
+ /// Defaults to the source location.
+ ///
+ /// @param options Option flags to control the operation. <<options?>>
+
+ static void DuplicateSubtree ( const TXMPMeta<tStringObj> & source,
+ TXMPMeta<tStringObj> * dest,
+ XMP_StringPtr sourceNS,
+ XMP_StringPtr sourceRoot,
+ XMP_StringPtr destNS = 0,
+ XMP_StringPtr destRoot = 0,
+ XMP_OptionBits options = 0 );
+
+ /// @}
+
+ // =============================================================================================
+
+private:
+
+ static void SetClientString ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen );
+
+}; // class TXMPUtils
+
+// =================================================================================================
+
+#endif // __TXMPUtils_hpp__
diff --git a/gpr/source/lib/xmp_core/public/include/XMP.hpp b/gpr/source/lib/xmp_core/public/include/XMP.hpp
new file mode 100644
index 0000000..8ec39eb
--- /dev/null
+++ b/gpr/source/lib/xmp_core/public/include/XMP.hpp
@@ -0,0 +1,98 @@
+#ifndef __XMP_hpp__
+#define __XMP_hpp__ 1
+
+// =================================================================================================
+// Copyright 2002 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.
+// =================================================================================================
+
+// ================================================================================================
+/// \file XMP.hpp
+/// \brief Overall header file for the XMP Toolkit
+///
+/// This is an overall header file, the only one that C++ clients should include.
+///
+/// The full client API is in the \c TXMPMeta.hpp, \c TXMPIterator.hpp, \c TXMPUtils.hpp headers.
+/// Read these for information, but do not include them directly. The \c TXMP... classes are C++
+/// template classes that must be instantiated with a string class such as \c std::string. The
+/// string class is used to return text strings for property values, serialized XMP, and so on.
+/// Clients must also compile \c XMP.incl_cpp to ensure that all client-side glue code is generated.
+/// This should be done by including it in exactly one client source file.
+///
+/// There are two C preprocessor macros that simplify use of the templates:
+///
+/// \li \c TXMP_STRING_TYPE - Define this as the string class to use with the template. You will get
+/// the template headers included and typedefs (\c SXMPMeta, and so on) to use in your code.
+///
+/// \li \c TXMP_EXPAND_INLINE - Define this as 1 if you want to have the template functions expanded
+/// inline in your code. Leave it undefined, or defined as 0, to use out-of-line instantiations of
+/// the template functions. Compiling \c XMP.incl_cpp generates explicit out-of-line
+/// instantiations if \c TXMP_EXPAND_INLINE is off.
+///
+/// The template parameter, class \c tStringObj, must have the following member functions (which
+/// match those for \c std::string):
+///
+/// <pre>
+/// tStringObj& assign ( const char * str, size_t len )
+/// size_t size() const
+/// const char * c_str() const
+/// </pre>
+///
+/// The string class must be suitable for at least UTF-8. This is the encoding used for all general
+/// values, and is the default encoding for serialized XMP. The string type must also be suitable
+/// for UTF-16 or UTF-32 if those serialization encodings are used. This mainly means tolerating
+/// embedded 0 bytes, which \c std::string does.
+// ================================================================================================
+
+/// /c XMP_Environment.h must be the first included header.
+#include "XMP_Environment.h"
+
+#include "XMP_Version.h"
+#include "XMP_Const.h"
+
+#if XMP_WinBuild
+ #if XMP_DebugBuild
+ #pragma warning ( push, 4 )
+ #else
+ #pragma warning ( push, 3 )
+ #endif
+ #pragma warning ( disable : 4702 ) // unreachable code
+ #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning)
+#endif
+
+#if defined ( TXMP_STRING_TYPE )
+
+ #include "TXMPMeta.hpp"
+ #include "TXMPIterator.hpp"
+ #include "TXMPUtils.hpp"
+ typedef class TXMPMeta <TXMP_STRING_TYPE> SXMPMeta; // For client convenience.
+ typedef class TXMPIterator <TXMP_STRING_TYPE> SXMPIterator;
+ typedef class TXMPUtils <TXMP_STRING_TYPE> SXMPUtils;
+ #if TXMP_EXPAND_INLINE
+ #error "TXMP_EXPAND_INLINE is not working at present. Please don't use it."
+ #include "client-glue/TXMPMeta.incl_cpp"
+ #include "client-glue/TXMPIterator.incl_cpp"
+ #include "client-glue/TXMPUtils.incl_cpp"
+ #include "client-glue/TXMPFiles.incl_cpp"
+ #endif
+
+ #if XMP_INCLUDE_XMPFILES
+ #include "TXMPFiles.hpp" // ! Needs typedef for SXMPMeta.
+ typedef class TXMPFiles <TXMP_STRING_TYPE> SXMPFiles;
+ #if TXMP_EXPAND_INLINE
+ #include "client-glue/TXMPFiles.incl_cpp"
+ #endif
+ #endif
+
+#endif // TXMP_STRING_TYPE
+
+#if XMP_WinBuild
+ #pragma warning ( pop )
+#endif
+
+// =================================================================================================
+
+#endif // __XMP_hpp__
diff --git a/gpr/source/lib/xmp_core/public/include/XMP.incl_cpp b/gpr/source/lib/xmp_core/public/include/XMP.incl_cpp
new file mode 100644
index 0000000..fe2a6fa
--- /dev/null
+++ b/gpr/source/lib/xmp_core/public/include/XMP.incl_cpp
@@ -0,0 +1,69 @@
+#ifndef __XMP_incl_cpp__
+#define __XMP_incl_cpp__ 1
+
+// =================================================================================================
+// ADOBE SYSTEMS INCORPORATED
+// Copyright 2002 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.
+// =================================================================================================
+
+// ================================================================================================
+/// \file XMP.incl_cpp
+/// \brief Overall client glue file for the XMP toolkit.
+///
+/// This is an overall client source file of XMP toolkit glue, the only XMP-specific one that
+/// clients should build in projects. This ensures that all of the client-side glue code for the
+/// XMP toolkit gets compiled.
+///
+/// You cannot compile this file directly, because the template's string type must be declared and
+/// only the client can do that. Instead, include this in some other source file. For example,
+/// to use <tt>std::string</tt> you only need these two lines:
+///
+/// \code
+/// #include <string>
+/// #include "XMP.incl_cpp"
+/// \endcode
+
+
+#include "XMP.hpp" // ! This must be the first include!
+
+#define XMP_ClientBuild 1
+
+#if XMP_WinBuild
+ #if XMP_DebugBuild
+ #pragma warning ( push, 4 )
+ #else
+ #pragma warning ( push, 3 )
+ #endif
+
+ #pragma warning ( disable : 4127 ) // conditional expression is constant
+ #pragma warning ( disable : 4189 ) // local variable is initialized but not referenced
+ #pragma warning ( disable : 4702 ) // unreachable code
+ #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning)
+#endif
+
+#if defined ( TXMP_STRING_TYPE ) && (! TXMP_EXPAND_INLINE)
+
+ // We're using a single out of line instantiation. Do it here.
+
+ #include "client-glue/TXMPMeta.incl_cpp"
+ #include "client-glue/TXMPIterator.incl_cpp"
+ #include "client-glue/TXMPUtils.incl_cpp"
+ template class TXMPMeta <TXMP_STRING_TYPE>;
+ template class TXMPIterator <TXMP_STRING_TYPE>;
+ template class TXMPUtils <TXMP_STRING_TYPE>;
+ #if XMP_INCLUDE_XMPFILES
+ #include "client-glue/TXMPFiles.incl_cpp"
+ template class TXMPFiles <TXMP_STRING_TYPE>;
+ #endif
+
+#endif
+
+#if XMP_WinBuild
+ #pragma warning ( pop )
+#endif
+
+#endif // __XMP_incl_cpp__
diff --git a/gpr/source/lib/xmp_core/public/include/XMP_Const.h b/gpr/source/lib/xmp_core/public/include/XMP_Const.h
new file mode 100644
index 0000000..eadc267
--- /dev/null
+++ b/gpr/source/lib/xmp_core/public/include/XMP_Const.h
@@ -0,0 +1,1560 @@
+#ifndef __XMP_Const_h__
+#define __XMP_Const_h__ 1
+
+// =================================================================================================
+// Copyright 2002 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.
+// =================================================================================================
+
+#include "XMP_Environment.h"
+
+ #include <stddef.h>
+
+#if XMP_MacBuild | XMP_iOSBuild // ! No stdint.h on Windows and some UNIXes.
+ #include <stdint.h>
+#endif
+#if XMP_UNIXBuild // hopefully an inttypes.h on all UNIXes...
+ #include <inttypes.h>
+#endif
+
+
+#if __cplusplus
+extern "C" {
+#endif
+
+// =================================================================================================
+/// \file XMP_Const.h
+/// \brief Common C/C++ types and constants for the XMP toolkit.
+// =================================================================================================
+
+// =================================================================================================
+// Basic types and constants
+// =========================
+
+// The XMP_... types are used on the off chance that the ..._t types present a problem. In that
+// case only the declarations of the XMP_... types needs to change, not all of the uses. These
+// types are used where fixed sizes are required in order to have a known ABI for a DLL build.
+
+#if XMP_MacBuild | XMP_iOSBuild
+
+ typedef int8_t XMP_Int8;
+ typedef int16_t XMP_Int16;
+ typedef int32_t XMP_Int32;
+ typedef int64_t XMP_Int64;
+
+ typedef uint8_t XMP_Uns8;
+ typedef uint16_t XMP_Uns16;
+ typedef uint32_t XMP_Uns32;
+ typedef uint64_t XMP_Uns64;
+
+#elif XMP_WinBuild
+
+ typedef signed char XMP_Int8;
+ typedef signed short XMP_Int16;
+ typedef signed long XMP_Int32;
+ typedef signed long long XMP_Int64;
+
+ typedef unsigned char XMP_Uns8;
+ typedef unsigned short XMP_Uns16;
+ typedef unsigned long XMP_Uns32;
+ typedef unsigned long long XMP_Uns64;
+
+#elif XMP_UNIXBuild
+
+ #if ! XMP_64
+
+ typedef signed char XMP_Int8;
+ typedef signed short XMP_Int16;
+ typedef signed long XMP_Int32;
+ typedef signed long long XMP_Int64;
+
+ typedef unsigned char XMP_Uns8;
+ typedef unsigned short XMP_Uns16;
+ typedef unsigned long XMP_Uns32;
+ typedef unsigned long long XMP_Uns64;
+
+ #else
+
+ typedef signed char XMP_Int8;
+ typedef signed short XMP_Int16;
+ typedef signed int XMP_Int32;
+ typedef signed long long XMP_Int64;
+
+ typedef unsigned char XMP_Uns8;
+ typedef unsigned short XMP_Uns16;
+ typedef unsigned int XMP_Uns32;
+ typedef unsigned long long XMP_Uns64;
+
+ #endif
+
+#else
+
+ #error "XMP environment error - must define one of XMP_MacBuild, XMP_WinBuild, XMP_UNIXBuild or XMP_iOSBuild"
+
+#endif
+
+typedef XMP_Uns8 XMP_Bool;
+
+const XMP_Uns8 kXMP_Bool_False = 0;
+
+#define ConvertXMP_BoolToBool(a) (a) != kXMP_Bool_False
+#define ConvertBoolToXMP_Bool(a) (a) ? !kXMP_Bool_False : kXMP_Bool_False
+
+static const XMP_Uns8 Min_XMP_Uns8 = ( (XMP_Uns8) 0x00 );
+static const XMP_Uns8 Max_XMP_Uns8 = ( (XMP_Uns8) 0xFF );
+static const XMP_Uns16 Min_XMP_Uns16 = ( (XMP_Uns16) 0x00 );
+static const XMP_Uns16 Max_XMP_Uns16 = ( (XMP_Uns16) 0xFFFF );
+static const XMP_Uns32 Min_XMP_Uns32 = ( (XMP_Uns32) 0x00 );
+static const XMP_Uns32 Max_XMP_Uns32 = ( (XMP_Uns32) 0xFFFFFFFF );
+static const XMP_Uns64 Min_XMP_Uns64 = ( (XMP_Uns64) 0x00 );
+static const XMP_Uns64 Max_XMP_Uns64 = ( (XMP_Uns64) 0xFFFFFFFFFFFFFFFFLL );
+
+static const XMP_Int8 Min_XMP_Int8 = ( (XMP_Int8) 0x80 );
+static const XMP_Int8 Max_XMP_Int8 = ( (XMP_Int8) 0x7F );
+static const XMP_Int16 Min_XMP_Int16 = ( (XMP_Int16) 0x8000 );
+static const XMP_Int16 Max_XMP_Int16 = ( (XMP_Int16) 0x7FFF );
+static const XMP_Int32 Min_XMP_Int32 = ( (XMP_Int32) 0x80000000 );
+static const XMP_Int32 Max_XMP_Int32 = ( (XMP_Int32) 0x7FFFFFFF );
+static const XMP_Int64 Min_XMP_Int64 = ( (XMP_Int64) 0x8000000000000000LL );
+static const XMP_Int64 Max_XMP_Int64 = ( (XMP_Int64) 0x7FFFFFFFFFFFFFFFLL );
+
+
+/// An "ABI safe" pointer to the internal part of an XMP object. Use to pass an XMP object across
+/// client DLL boundaries. See \c TXMPMeta::GetInternalRef().
+typedef struct __XMPMeta__ * XMPMetaRef;
+
+/// An "ABI safe" pointer to the internal part of an XMP iteration object. Use to pass an XMP
+/// iteration object across client DLL boundaries. See \c TXMPIterator.
+typedef struct __XMPIterator__ * XMPIteratorRef;
+
+/// An "ABI safe" pointer to the internal part of an XMP document operations object. Use to pass an
+/// XMP document operations object across client DLL boundaries. See \c TXMPDocOps.
+typedef struct __XMPDocOps__ * XMPDocOpsRef;
+
+/// An "ABI safe" pointer to the internal part of an XMP file-handling object. Use to pass an XMP
+/// file-handling object across client DLL boundaries. See \c TXMPFiles.
+typedef struct __XMPFiles__ * XMPFilesRef;
+
+// =================================================================================================
+
+/// \name General scalar types and constants
+/// @{
+
+/// \typedef XMP_StringPtr
+/// \brief The type for input string parameters. A <tt>const char *</tt>, a null-terminated UTF-8
+/// string.
+
+/// \typedef XMP_StringLen
+/// \brief The type for string length parameters. A 32-bit unsigned integer, as big as will be
+/// practically needed.
+
+/// \typedef XMP_Index
+/// \brief The type for offsets and indices. A 32-bit signed integer. It is signed to allow -1 for
+/// loop termination.
+
+/// \typedef XMP_OptionBits
+/// \brief The type for a collection of 32 flag bits. Individual flags are defined as enum value bit
+/// masks; see \c #kXMP_PropValueIsURI and following. A number of macros provide common set or set
+/// operations, such as \c XMP_PropIsSimple. For other tests use an expression like <code>options &
+/// kXMP_<theOption></code>. When passing multiple option flags use the bitwise-OR operator. '|',
+/// not the arithmatic plus, '+'.
+
+typedef const char * XMP_StringPtr; // Points to a null terminated UTF-8 string.
+typedef XMP_Uns32 XMP_StringLen;
+typedef XMP_Int32 XMP_Index; // Signed, sometimes -1 is handy.
+typedef XMP_Uns32 XMP_OptionBits; // Used as 32 individual bits.
+
+/// \def kXMP_TrueStr
+/// \brief The canonical true string value for Booleans in serialized XMP.
+///
+/// Code that converts from string to bool should be case insensitive, and also allow "1".
+
+/// \def kXMP_FalseStr
+/// \brief The canonical false string value for Booleans in serialized XMP.
+///
+/// Code that converts from string to bool should be case insensitive, and also allow "0".
+
+#define kXMP_TrueStr "True" // Serialized XMP spellings, not for the type bool.
+#define kXMP_FalseStr "False"
+
+/// Type for yes/no/maybe answers. The values are picked to allow Boolean-like usage. The yes and
+/// values are true (non-zero), the no value is false (zero).
+enum {
+ /// The part or parts have definitely changed.
+ kXMPTS_Yes = 1,
+ /// The part or parts have definitely not changed.
+ kXMPTS_No = 0,
+ /// The part or parts might, or might not, have changed.
+ kXMPTS_Maybe = -1
+};
+typedef XMP_Int8 XMP_TriState;
+
+/// @}
+
+// =================================================================================================
+
+/// \struct XMP_DateTime
+/// \brief The expanded type for a date and time.
+///
+/// Dates and time in the serialized XMP are ISO 8601 strings. The \c XMP_DateTime struct allows
+/// easy conversion with other formats.
+///
+/// All of the fields are 32 bit, even though most could be 8 bit. This avoids overflow when doing
+/// carries for arithmetic or normalization. All fields have signed values for the same reasons.
+///
+/// Date-time values are occasionally used with only a date or only a time component. A date without
+/// a time has zeros in the \c XMP_DateTime struct for all time fields. A time without a date has
+/// zeros for all date fields (year, month, and day).
+///
+/// \c TXMPUtils provides utility functions for manipulating date-time values.
+///
+/// @see \c TXMPUtils::ConvertToDate(), \c TXMPUtils::ConvertFromDate(),
+/// \c TXMPUtils::CompareDateTime(), \c TXMPUtils::ConvertToLocalTime(),
+/// \c TXMPUtils::ConvertToUTCTime(), \c TXMPUtils::CurrentDateTime(),
+/// \c TXMPUtils::SetTimeZone()
+
+struct XMP_DateTime {
+
+ /// The year, can be negative.
+ XMP_Int32 year;
+
+ /// The month in the range 1..12.
+ XMP_Int32 month;
+
+ /// The day of the month in the range 1..31.
+ XMP_Int32 day;
+
+ /// The hour in the range 0..23.
+ XMP_Int32 hour;
+
+ /// The minute in the range 0..59.
+ XMP_Int32 minute;
+
+ /// The second in the range 0..59.
+ XMP_Int32 second;
+
+ /// Is the date portion meaningful?
+ XMP_Bool hasDate;
+
+ /// Is the time portion meaningful?
+ XMP_Bool hasTime;
+
+ /// Is the time zone meaningful?
+ XMP_Bool hasTimeZone;
+
+ /// The "sign" of the time zone, \c #kXMP_TimeIsUTC (0) means UTC, \c #kXMP_TimeWestOfUTC (-1)
+ /// is west, \c #kXMP_TimeEastOfUTC (+1) is east.
+ XMP_Int8 tzSign;
+
+ /// The time zone hour in the range 0..23.
+ XMP_Int32 tzHour;
+
+ /// The time zone minute in the range 0..59.
+ XMP_Int32 tzMinute;
+
+ /// Nanoseconds within a second, often left as zero.
+ XMP_Int32 nanoSecond;
+
+ #if __cplusplus
+ XMP_DateTime() : year(0), month(0), day(0), hour(0), minute(0), second(0),
+ hasDate(false),hasTime(false), hasTimeZone(false), tzSign(0), tzHour(0), tzMinute(0), nanoSecond(0){};
+ #endif
+
+};
+
+/// Constant values for \c XMP_DateTime::tzSign field.
+enum {
+ /// Time zone is west of UTC.
+ kXMP_TimeWestOfUTC = -1,
+ /// UTC time.
+ kXMP_TimeIsUTC = 0,
+ /// Time zone is east of UTC.
+ kXMP_TimeEastOfUTC = +1
+};
+
+#define XMPDateTime_IsDateOnly(dt) ((dt).hasDate & (! (dt).hasTime))
+#define XMPDateTime_IsTimeOnly(dt) ((dt).hasTime & (! (dt).hasDate))
+
+#define XMPDateTime_ClearTimeZone(dt) { (dt).hasTimeZone = (dt).tzSign = (dt).tzHour = (dt).tzMinute = 0; }
+
+// =================================================================================================
+// Standard namespace URI constants
+// ================================
+
+/// \name XML namespace constants for standard XMP schema.
+/// @{
+///
+/// \def kXMP_NS_XMP
+/// \brief The XML namespace for the XMP "basic" schema.
+///
+/// \def kXMP_NS_XMP_Rights
+/// \brief The XML namespace for the XMP copyright schema.
+///
+/// \def kXMP_NS_XMP_MM
+/// \brief The XML namespace for the XMP digital asset management schema.
+///
+/// \def kXMP_NS_XMP_BJ
+/// \brief The XML namespace for the job management schema.
+///
+/// \def kXMP_NS_XMP_T
+/// \brief The XML namespace for the XMP text document schema.
+///
+/// \def kXMP_NS_XMP_T_PG
+/// \brief The XML namespace for the XMP paged document schema.
+///
+/// \def kXMP_NS_PDF
+/// \brief The XML namespace for the PDF schema.
+///
+/// \def kXMP_NS_Photoshop
+/// \brief The XML namespace for the Photoshop custom schema.
+///
+/// \def kXMP_NS_EXIF
+/// \brief The XML namespace for Adobe's EXIF schema.
+///
+/// \def kXMP_NS_TIFF
+/// \brief The XML namespace for Adobe's TIFF schema.
+///
+/// @}
+
+#define kXMP_NS_XMP "http://ns.adobe.com/xap/1.0/"
+
+#define kXMP_NS_XMP_Rights "http://ns.adobe.com/xap/1.0/rights/"
+#define kXMP_NS_XMP_MM "http://ns.adobe.com/xap/1.0/mm/"
+#define kXMP_NS_XMP_BJ "http://ns.adobe.com/xap/1.0/bj/"
+
+#define kXMP_NS_PDF "http://ns.adobe.com/pdf/1.3/"
+#define kXMP_NS_Photoshop "http://ns.adobe.com/photoshop/1.0/"
+#define kXMP_NS_PSAlbum "http://ns.adobe.com/album/1.0/"
+#define kXMP_NS_EXIF "http://ns.adobe.com/exif/1.0/"
+#define kXMP_NS_EXIF_Aux "http://ns.adobe.com/exif/1.0/aux/"
+#define kXMP_NS_TIFF "http://ns.adobe.com/tiff/1.0/"
+#define kXMP_NS_PNG "http://ns.adobe.com/png/1.0/"
+#define kXMP_NS_SWF "http://ns.adobe.com/swf/1.0/"
+#define kXMP_NS_JPEG "http://ns.adobe.com/jpeg/1.0/"
+#define kXMP_NS_JP2K "http://ns.adobe.com/jp2k/1.0/"
+#define kXMP_NS_CameraRaw "http://ns.adobe.com/camera-raw-settings/1.0/"
+#define kXMP_NS_DM "http://ns.adobe.com/xmp/1.0/DynamicMedia/"
+#define kXMP_NS_Script "http://ns.adobe.com/xmp/1.0/Script/"
+#define kXMP_NS_ASF "http://ns.adobe.com/asf/1.0/"
+#define kXMP_NS_WAV "http://ns.adobe.com/xmp/wav/1.0/"
+#define kXMP_NS_BWF "http://ns.adobe.com/bwf/bext/1.0/"
+#define kXMP_NS_AEScart "http://ns.adobe.com/aes/cart/"
+#define kXMP_NS_RIFFINFO "http://ns.adobe.com/riff/info/"
+
+#define kXMP_NS_XMP_Note "http://ns.adobe.com/xmp/note/"
+
+#define kXMP_NS_AdobeStockPhoto "http://ns.adobe.com/StockPhoto/1.0/"
+#define kXMP_NS_CreatorAtom "http://ns.adobe.com/creatorAtom/1.0/"
+
+#define kXMP_NS_ExifEX "http://cipa.jp/exif/1.0/"
+
+/// \name XML namespace constants for qualifiers and structured property fields.
+/// @{
+///
+/// \def kXMP_NS_XMP_IdentifierQual
+/// \brief The XML namespace for qualifiers of the xmp:Identifier property.
+///
+/// \def kXMP_NS_XMP_Dimensions
+/// \brief The XML namespace for fields of the Dimensions type.
+///
+/// \def kXMP_NS_XMP_Image
+/// \brief The XML namespace for fields of a graphical image. Used for the Thumbnail type.
+///
+/// \def kXMP_NS_XMP_ResourceEvent
+/// \brief The XML namespace for fields of the ResourceEvent type.
+///
+/// \def kXMP_NS_XMP_ResourceRef
+/// \brief The XML namespace for fields of the ResourceRef type.
+///
+/// \def kXMP_NS_XMP_ST_Version
+/// \brief The XML namespace for fields of the Version type.
+///
+/// \def kXMP_NS_XMP_ST_Job
+/// \brief The XML namespace for fields of the JobRef type.
+///
+/// @}
+
+#define kXMP_NS_XMP_IdentifierQual "http://ns.adobe.com/xmp/Identifier/qual/1.0/"
+#define kXMP_NS_XMP_Dimensions "http://ns.adobe.com/xap/1.0/sType/Dimensions#"
+#define kXMP_NS_XMP_Text "http://ns.adobe.com/xap/1.0/t/"
+#define kXMP_NS_XMP_PagedFile "http://ns.adobe.com/xap/1.0/t/pg/"
+#define kXMP_NS_XMP_Graphics "http://ns.adobe.com/xap/1.0/g/"
+#define kXMP_NS_XMP_Image "http://ns.adobe.com/xap/1.0/g/img/"
+#define kXMP_NS_XMP_Font "http://ns.adobe.com/xap/1.0/sType/Font#"
+#define kXMP_NS_XMP_ResourceEvent "http://ns.adobe.com/xap/1.0/sType/ResourceEvent#"
+#define kXMP_NS_XMP_ResourceRef "http://ns.adobe.com/xap/1.0/sType/ResourceRef#"
+#define kXMP_NS_XMP_ST_Version "http://ns.adobe.com/xap/1.0/sType/Version#"
+#define kXMP_NS_XMP_ST_Job "http://ns.adobe.com/xap/1.0/sType/Job#"
+#define kXMP_NS_XMP_ManifestItem "http://ns.adobe.com/xap/1.0/sType/ManifestItem#"
+
+// Deprecated XML namespace constants
+#define kXMP_NS_XMP_T "http://ns.adobe.com/xap/1.0/t/"
+#define kXMP_NS_XMP_T_PG "http://ns.adobe.com/xap/1.0/t/pg/"
+#define kXMP_NS_XMP_G_IMG "http://ns.adobe.com/xap/1.0/g/img/"
+
+/// \name XML namespace constants from outside Adobe.
+/// @{
+///
+/// \def kXMP_NS_DC
+/// \brief The XML namespace for the Dublin Core schema.
+///
+/// \def kXMP_NS_IPTCCore
+/// \brief The XML namespace for the IPTC Core schema.
+///
+/// \def kXMP_NS_IPTCExt
+/// \brief The XML namespace for the IPTC Extension schema.
+///
+/// \def kXMP_NS_RDF
+/// \brief The XML namespace for RDF.
+///
+/// \def kXMP_NS_XML
+/// \brief The XML namespace for XML.
+///
+/// @}
+
+#define kXMP_NS_DC "http://purl.org/dc/elements/1.1/"
+
+#define kXMP_NS_IPTCCore "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/"
+#define kXMP_NS_IPTCExt "http://iptc.org/std/Iptc4xmpExt/2008-02-29/"
+
+#define kXMP_NS_DICOM "http://ns.adobe.com/DICOM/"
+
+#define kXMP_NS_PLUS "http://ns.useplus.org/ldf/xmp/1.0/"
+
+#define kXMP_NS_PDFA_Schema "http://www.aiim.org/pdfa/ns/schema#"
+#define kXMP_NS_PDFA_Property "http://www.aiim.org/pdfa/ns/property#"
+#define kXMP_NS_PDFA_Type "http://www.aiim.org/pdfa/ns/type#"
+#define kXMP_NS_PDFA_Field "http://www.aiim.org/pdfa/ns/field#"
+#define kXMP_NS_PDFA_ID "http://www.aiim.org/pdfa/ns/id/"
+#define kXMP_NS_PDFA_Extension "http://www.aiim.org/pdfa/ns/extension/"
+
+#define kXMP_NS_PDFX "http://ns.adobe.com/pdfx/1.3/"
+#define kXMP_NS_PDFX_ID "http://www.npes.org/pdfx/ns/id/"
+
+#define kXMP_NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+#define kXMP_NS_XML "http://www.w3.org/XML/1998/namespace"
+
+// =================================================================================================
+// Enums and macros used for option bits
+// =====================================
+
+/// \name Macros for standard option selections.
+/// @{
+///
+/// \def kXMP_ArrayLastItem
+/// \brief Options macro accesses last array item.
+///
+/// \def kXMP_UseNullTermination
+/// \brief Options macro sets string style.
+///
+/// \def kXMP_NoOptions
+/// \brief Options macro clears all property-type bits.
+///
+/// @}
+
+#define kXMP_ArrayLastItem ((XMP_Index)(-1L))
+#define kXMP_UseNullTermination ((XMP_StringLen)(~0UL))
+#define kXMP_NoOptions ((XMP_OptionBits)0UL)
+
+/// \name Macros for setting and testing general option bits.
+/// @{
+///
+/// \def XMP_SetOption
+/// \brief Macro sets an option flag bit.
+/// \param var A variable storing an options flag.
+/// \param opt The bit-flag constant to set.
+///
+/// \def XMP_ClearOption
+/// \brief Macro clears an option flag bit.
+/// \param var A variable storing an options flag.
+/// \param opt The bit-flag constant to clear.
+///
+/// \def XMP_TestOption
+/// \brief Macro reports whether an option flag bit is set.
+/// \param var A variable storing an options flag.
+/// \param opt The bit-flag constant to test.
+/// \return True if the bit is set.
+///
+/// \def XMP_OptionIsSet
+/// \brief Macro reports whether an option flag bit is set.
+/// \param var A variable storing an options flag.
+/// \param opt The bit-flag constant to test.
+/// \return True if the bit is set.
+///
+/// \def XMP_OptionIsClear
+/// \brief Macro reports whether an option flag bit is clear.
+/// \param var A variable storing an options flag.
+/// \param opt The bit-flag constant to test.
+/// \return True if the bit is clear.
+///
+/// @}
+
+#define XMP_SetOption(var,opt) var |= (opt)
+#define XMP_ClearOption(var,opt) var &= ~(opt)
+#define XMP_TestOption(var,opt) (((var) & (opt)) != 0)
+#define XMP_OptionIsSet(var,opt) (((var) & (opt)) != 0)
+#define XMP_OptionIsClear(var,opt) (((var) & (opt)) == 0)
+
+/// \name Macros for setting and testing specific option bits.
+/// @{
+///
+/// \def XMP_PropIsSimple
+/// \brief Macro reports the property type specified by an options flag.
+/// \param opt The options flag to check.
+///
+/// \def XMP_PropIsStruct
+/// \brief Macro reports the property type specified by an options flag.
+/// \param opt The options flag to check.
+///
+/// \def XMP_PropIsArray
+/// \brief Macro reports the property type specified by an options flag.
+/// \param opt The options flag to check.
+///
+/// \def XMP_ArrayIsUnordered
+/// \brief Macro reports the property type specified by an options flag.
+/// \param opt The options flag to check.
+///
+/// \def XMP_ArrayIsOrdered
+/// \brief Macro reports the property type specified by an options flag.
+/// \param opt The options flag to check.
+///
+/// \def XMP_ArrayIsAlternate
+/// \brief Macro reports the property type specified by an options flag.
+/// \param opt The options flag to check.
+///
+/// \def XMP_ArrayIsAltText
+/// \brief Macro reports the property type specified by an options flag.
+/// \param opt The options flag to check.
+///
+/// \def XMP_PropHasQualifiers
+/// \brief Macro reports the property type specified by an options flag.
+/// \param opt The options flag to check.
+///
+/// \def XMP_PropIsQualifier
+/// \brief Macro reports the property type specified by an options flag.
+/// \param opt The options flag to check.
+///
+/// \def XMP_PropHasLang
+/// \brief Macro reports the property type specified by an options flag.
+/// \param opt The options flag to check.
+///
+/// \def XMP_NodeIsSchema
+/// \brief Macro reports the property type specified by an options flag.
+/// \param opt The options flag to check.
+///
+/// \def XMP_PropIsAlias
+/// \brief Macro reports the property type specified by an options flag.
+/// \param opt The options flag to check.
+///
+/// @}
+
+#define XMP_PropIsSimple(opt) (((opt) & kXMP_PropCompositeMask) == 0)
+#define XMP_PropIsStruct(opt) (((opt) & kXMP_PropValueIsStruct) != 0)
+#define XMP_PropIsArray(opt) (((opt) & kXMP_PropValueIsArray) != 0)
+
+#define XMP_ArrayIsUnordered(opt) (((opt) & kXMP_PropArrayIsOrdered) == 0)
+#define XMP_ArrayIsOrdered(opt) (((opt) & kXMP_PropArrayIsOrdered) != 0)
+#define XMP_ArrayIsAlternate(opt) (((opt) & kXMP_PropArrayIsAlternate) != 0)
+#define XMP_ArrayIsAltText(opt) (((opt) & kXMP_PropArrayIsAltText) != 0)
+
+#define XMP_PropHasQualifiers(opt) (((opt) & kXMP_PropHasQualifiers) != 0)
+#define XMP_PropIsQualifier(opt) (((opt) & kXMP_PropIsQualifier) != 0)
+#define XMP_PropHasLang(opt) (((opt) & kXMP_PropHasLang) != 0)
+
+#define XMP_NodeIsSchema(opt) (((opt) & kXMP_SchemaNode) != 0)
+#define XMP_PropIsAlias(opt) (((opt) & kXMP_PropIsAlias) != 0)
+
+// -------------------------------------------------------------------------------------------------
+
+/// Option bit flags for the \c TXMPMeta property accessor functions.
+enum {
+
+ /// The XML string form of the property value is a URI, use rdf:resource attribute. DISCOURAGED
+ kXMP_PropValueIsURI = 0x00000002UL,
+
+ // ------------------------------------------------------
+ // Options relating to qualifiers attached to a property.
+
+ /// The property has qualifiers, includes \c rdf:type and \c xml:lang.
+ kXMP_PropHasQualifiers = 0x00000010UL,
+
+ /// This is a qualifier for some other property, includes \c rdf:type and \c xml:lang.
+ /// Qualifiers can have arbitrary structure, and can themselves have qualifiers. If the
+ /// qualifier itself has a structured value, this flag is only set for the top node of the
+ /// qualifier's subtree.
+ kXMP_PropIsQualifier = 0x00000020UL,
+
+ /// Implies \c #kXMP_PropHasQualifiers, property has \c xml:lang.
+ kXMP_PropHasLang = 0x00000040UL,
+
+ /// Implies \c #kXMP_PropHasQualifiers, property has \c rdf:type.
+ kXMP_PropHasType = 0x00000080UL,
+
+ // --------------------------------------------
+ // Options relating to the data structure form.
+
+ /// The value is a structure with nested fields.
+ kXMP_PropValueIsStruct = 0x00000100UL,
+
+ /// The value is an array (RDF alt/bag/seq). The "ArrayIs..." flags identify specific types
+ /// of array; default is a general unordered array, serialized using an \c rdf:Bag container.
+ kXMP_PropValueIsArray = 0x00000200UL,
+
+ /// The item order does not matter.
+ kXMP_PropArrayIsUnordered = kXMP_PropValueIsArray,
+
+ /// Implies \c #kXMP_PropValueIsArray, item order matters. It is serialized using an \c rdf:Seq container.
+ kXMP_PropArrayIsOrdered = 0x00000400UL,
+
+ /// Implies \c #kXMP_PropArrayIsOrdered, items are alternates. It is serialized using an \c rdf:Alt container.
+ kXMP_PropArrayIsAlternate = 0x00000800UL,
+
+ // ------------------------------------
+ // Additional struct and array options.
+
+ /// Implies \c #kXMP_PropArrayIsAlternate, items are localized text. Each array element is a
+ /// simple property with an \c xml:lang attribute.
+ kXMP_PropArrayIsAltText = 0x00001000UL,
+
+ // kXMP_InsertBeforeItem = 0x00004000UL, ! Used by SetXyz functions.
+ // kXMP_InsertAfterItem = 0x00008000UL, ! Used by SetXyz functions.
+
+ // ----------------------------
+ // Other miscellaneous options.
+
+ /// This property is an alias name for another property. This is only returned by
+ /// \c TXMPMeta::GetProperty() and then only if the property name is simple, not an path expression.
+ kXMP_PropIsAlias = 0x00010000UL,
+
+ /// This property is the base value (actual) for a set of aliases.This is only returned by
+ /// \c TXMPMeta::GetProperty() and then only if the property name is simple, not an path expression.
+ kXMP_PropHasAliases = 0x00020000UL,
+
+ /// The value of this property is "owned" by the application, and should not generally be editable in a UI.
+ kXMP_PropIsInternal = 0x00040000UL,
+
+ /// The value of this property is not derived from the document content.
+ kXMP_PropIsStable = 0x00100000UL,
+
+ /// The value of this property is derived from the document content.
+ kXMP_PropIsDerived = 0x00200000UL,
+
+ // kXMPUtil_AllowCommas = 0x10000000UL, ! Used by TXMPUtils::CatenateArrayItems and ::SeparateArrayItems.
+ // kXMP_DeleteExisting = 0x20000000UL, ! Used by TXMPMeta::SetXyz functions to delete any pre-existing property.
+ // kXMP_SchemaNode = 0x80000000UL, ! Returned by iterators - #define to avoid warnings
+
+ // ------------------------------
+ // Masks that are multiple flags.
+
+ /// Property type bit-flag mask for all array types
+ kXMP_PropArrayFormMask = kXMP_PropValueIsArray | kXMP_PropArrayIsOrdered | kXMP_PropArrayIsAlternate | kXMP_PropArrayIsAltText,
+
+ /// Property type bit-flag mask for composite types (array and struct)
+ kXMP_PropCompositeMask = kXMP_PropValueIsStruct | kXMP_PropArrayFormMask,
+
+ /// Mask for bits that are reserved for transient use by the implementation.
+ kXMP_ImplReservedMask = 0x70000000L
+
+};
+
+#define kXMP_SchemaNode ((XMP_OptionBits)0x80000000UL)
+
+/// Option bit flags for the \c TXMPMeta property setting functions. These option bits are shared
+/// with the accessor functions:
+/// \li \c #kXMP_PropValueIsURI
+/// \li \c #kXMP_PropValueIsStruct
+/// \li \c #kXMP_PropValueIsArray
+/// \li \c #kXMP_PropArrayIsOrdered
+/// \li \c #kXMP_PropArrayIsAlternate
+/// \li \c #kXMP_PropArrayIsAltText
+enum {
+
+ /// Option for array item location: Insert a new item before the given index.
+ kXMP_InsertBeforeItem = 0x00004000UL,
+
+ /// Option for array item location: Insert a new item after the given index.
+ kXMP_InsertAfterItem = 0x00008000UL,
+
+ /// Delete any pre-existing property.
+ kXMP_DeleteExisting = 0x20000000UL,
+
+ /// Bit-flag mask for property-value option bits
+ kXMP_PropValueOptionsMask = kXMP_PropValueIsURI,
+
+ /// Bit-flag mask for array-item location bits
+ kXMP_PropArrayLocationMask = kXMP_InsertBeforeItem | kXMP_InsertAfterItem
+
+};
+
+// -------------------------------------------------------------------------------------------------
+
+/// Option bit flags for \c TXMPMeta::ParseFromBuffer().
+enum {
+
+ /// Require a surrounding \c x:xmpmeta element.
+ kXMP_RequireXMPMeta = 0x0001UL,
+
+ /// This is the not last input buffer for this parse stream.
+ kXMP_ParseMoreBuffers = 0x0002UL,
+
+ /// Do not reconcile alias differences, throw an exception.
+ kXMP_StrictAliasing = 0x0004UL
+
+};
+
+/// Option bit flags for \c TXMPMeta::SerializeToBuffer().
+enum {
+
+ // *** Option to remove empty struct/array, or leaf with empty value?
+
+ /// Omit the XML packet wrapper.
+ kXMP_OmitPacketWrapper = 0x0010UL,
+
+ /// Default is a writeable packet.
+ kXMP_ReadOnlyPacket = 0x0020UL,
+
+ /// Use a compact form of RDF.
+ kXMP_UseCompactFormat = 0x0040UL,
+
+ /// Use a canonical form of RDF.
+ kXMP_UseCanonicalFormat = 0x0080UL,
+
+ /// Include a padding allowance for a thumbnail image.
+ kXMP_IncludeThumbnailPad = 0x0100UL,
+
+ /// The padding parameter is the overall packet length.
+ kXMP_ExactPacketLength = 0x0200UL,
+
+ /// Omit all formatting whitespace.
+ kXMP_OmitAllFormatting = 0x0800UL,
+
+ /// Omit the x:xmpmeta element surrounding the rdf:RDF element.
+ kXMP_OmitXMPMetaElement = 0x1000UL,
+
+ /// Include a rdf Hash and Merged flag in x:xmpmeta element.
+ kXMP_IncludeRDFHash = 0x2000UL,
+
+ _XMP_LittleEndian_Bit = 0x0001UL, // ! Don't use directly, see the combined values below!
+ _XMP_UTF16_Bit = 0x0002UL,
+ _XMP_UTF32_Bit = 0x0004UL,
+
+ /// Bit-flag mask for encoding-type bits
+ kXMP_EncodingMask = 0x0007UL,
+
+ /// Use UTF8 encoding
+ kXMP_EncodeUTF8 = 0UL,
+
+ /// Use UTF16 big-endian encoding
+ kXMP_EncodeUTF16Big = _XMP_UTF16_Bit,
+
+ /// Use UTF16 little-endian encoding
+ kXMP_EncodeUTF16Little = _XMP_UTF16_Bit | _XMP_LittleEndian_Bit,
+
+ /// Use UTF32 big-endian encoding
+ kXMP_EncodeUTF32Big = _XMP_UTF32_Bit,
+
+ /// Use UTF13 little-endian encoding
+ kXMP_EncodeUTF32Little = _XMP_UTF32_Bit | _XMP_LittleEndian_Bit
+
+};
+
+// -------------------------------------------------------------------------------------------------
+
+/// Option bit flags for \c TXMPIterator construction.
+enum {
+
+ /// The low 8 bits are an enum of what data structure to iterate.
+ kXMP_IterClassMask = 0x00FFUL,
+
+ /// Iterate the property tree of a TXMPMeta object.
+ kXMP_IterProperties = 0x0000UL,
+
+ /// Iterate the global alias table.
+ kXMP_IterAliases = 0x0001UL,
+
+ /// Iterate the global namespace table.
+ kXMP_IterNamespaces = 0x0002UL,
+
+ /// Just do the immediate children of the root, default is subtree.
+ kXMP_IterJustChildren = 0x0100UL,
+
+ /// Just do the leaf nodes, default is all nodes in the subtree.
+ kXMP_IterJustLeafNodes = 0x0200UL,
+
+ /// Return just the leaf part of the path, default is the full path.
+ kXMP_IterJustLeafName = 0x0400UL,
+
+ /// Omit all qualifiers.
+ kXMP_IterOmitQualifiers = 0x1000UL
+
+};
+
+/// Option bit flags for \c TXMPIterator::Skip().
+enum {
+
+ /// Skip the subtree below the current node.
+ kXMP_IterSkipSubtree = 0x0001UL,
+
+ /// Skip the subtree below and remaining siblings of the current node.
+ kXMP_IterSkipSiblings = 0x0002UL
+
+};
+
+// -------------------------------------------------------------------------------------------------
+
+/// Option bit flags for \c TXMPUtils::CatenateArrayItems() and \c TXMPUtils::SeparateArrayItems().
+/// These option bits are shared with the accessor functions:
+/// \li \c #kXMP_PropValueIsArray,
+/// \li \c #kXMP_PropArrayIsOrdered,
+/// \li \c #kXMP_PropArrayIsAlternate,
+/// \li \c #kXMP_PropArrayIsAltText
+enum {
+
+ /// Allow commas in item values, default is separator.
+ kXMPUtil_AllowCommas = 0x10000000UL
+
+};
+
+/// Option bit flags for \c TXMPUtils::ApplyTemplate().
+enum {
+
+ /// Do all properties, default is just external properties.
+ kXMPTemplate_IncludeInternalProperties = 0x0001UL,
+
+ /// Perform a Replace operation, add new properties and modify existing ones.
+ kXMPTemplate_ReplaceExistingProperties = 0x0002UL,
+
+ /// Similar to Replace, also delete if the template has an empty value.
+ kXMPTemplate_ReplaceWithDeleteEmpty = 0x0004UL,
+
+ /// Perform an Add operation, add properties if they don't already exist.
+ kXMPTemplate_AddNewProperties = 0x0008UL,
+
+ /// Perform a Clear operation, keep named properties and delete everything else.
+ kXMPTemplate_ClearUnnamedProperties = 0x0010UL
+
+};
+
+/// Option bit flags for \c TXMPUtils::RemoveProperties() and \c TXMPUtils::AppendProperties().
+enum {
+
+ /// Do all properties, default is just external properties.
+ kXMPUtil_DoAllProperties = 0x0001UL,
+
+ /// Replace existing values, default is to leave them.
+ kXMPUtil_ReplaceOldValues = 0x0002UL,
+
+ /// Delete properties if the new value is empty.
+ kXMPUtil_DeleteEmptyValues = 0x0004UL,
+
+ /// Include aliases, default is just actual properties.
+ kXMPUtil_IncludeAliases = 0x0800UL
+
+};
+
+// =================================================================================================
+// Types and Constants for XMPFiles
+// ================================
+
+/// Seek mode constants for use with XMP_IO and inside XMPFiles library code.
+enum SeekMode { kXMP_SeekFromStart, kXMP_SeekFromCurrent, kXMP_SeekFromEnd };
+
+/// File format constants for use with XMPFiles.
+enum {
+
+ // ! Hex used to avoid gcc warnings. Leave the constants so the text reads big endian. There
+ // ! seems to be no decent way on UNIX to determine the target endianness at compile time.
+ // ! Forcing it on the client isn't acceptable.
+
+ // --------------------
+ // Public file formats.
+
+ /// Public file format constant: 'PDF '
+ kXMP_PDFFile = 0x50444620UL,
+ /// Public file format constant: 'PS ', general PostScript following DSC conventions
+ kXMP_PostScriptFile = 0x50532020UL,
+ /// Public file format constant: 'EPS ', encapsulated PostScript
+ kXMP_EPSFile = 0x45505320UL,
+
+ /// Public file format constant: 'JPEG'
+ kXMP_JPEGFile = 0x4A504547UL,
+ /// Public file format constant: 'JPX ', JPEG 2000, ISO 15444-1
+ kXMP_JPEG2KFile = 0x4A505820UL,
+ /// Public file format constant: 'TIFF'
+ kXMP_TIFFFile = 0x54494646UL,
+ /// Public file format constant: 'GIF '
+ kXMP_GIFFile = 0x47494620UL,
+ /// Public file format constant: 'PNG '
+ kXMP_PNGFile = 0x504E4720UL,
+
+ /// Public file format constant: 'SWF '
+ kXMP_SWFFile = 0x53574620UL,
+ /// Public file format constant: 'FLA '
+ kXMP_FLAFile = 0x464C4120UL,
+ /// Public file format constant: 'FLV '
+ kXMP_FLVFile = 0x464C5620UL,
+
+ /// Public file format constant: 'MOV ', Quicktime
+ kXMP_MOVFile = 0x4D4F5620UL,
+ /// Public file format constant: 'AVI '
+ kXMP_AVIFile = 0x41564920UL,
+ /// Public file format constant: 'CIN ', Cineon
+ kXMP_CINFile = 0x43494E20UL,
+ /// Public file format constant: 'WAV '
+ kXMP_WAVFile = 0x57415620UL,
+ /// Public file format constant: 'MP3 '
+ kXMP_MP3File = 0x4D503320UL,
+ /// Public file format constant: 'SES ', Audition session
+ kXMP_SESFile = 0x53455320UL,
+ /// Public file format constant: 'CEL ', Audition loop
+ kXMP_CELFile = 0x43454C20UL,
+ /// Public file format constant: 'MPEG'
+ kXMP_MPEGFile = 0x4D504547UL,
+ /// Public file format constant: 'MP2 '
+ kXMP_MPEG2File = 0x4D503220UL,
+ /// Public file format constant: 'MP4 ', ISO 14494-12 and -14
+ kXMP_MPEG4File = 0x4D503420UL,
+ /// Public file format constant: 'MXF '
+ kXMP_MXFFile = 0x4D584620UL,
+ /// Public file format constant: 'WMAV', Windows Media Audio and Video
+ kXMP_WMAVFile = 0x574D4156UL,
+ /// Public file format constant: 'AIFF'
+ kXMP_AIFFFile = 0x41494646UL,
+ /// Public file format constant: 'RED ', RED file format
+ kXMP_REDFile = 0x52454420UL,
+ /// Public file format constant: 'P2 ', a collection not really a single file
+ kXMP_P2File = 0x50322020UL,
+ /// Public file format constant: 'XDCF', a collection not really a single file
+ kXMP_XDCAM_FAMFile = 0x58444346UL,
+ /// Public file format constant: 'XDCS', a collection not really a single file
+ kXMP_XDCAM_SAMFile = 0x58444353UL,
+ /// Public file format constant: 'XDCX', a collection not really a single file
+ kXMP_XDCAM_EXFile = 0x58444358UL,
+ /// Public file format constant: 'AVHD', a collection not really a single file
+ kXMP_AVCHDFile = 0x41564844UL,
+ /// Public file format constant: 'SHDV', a collection not really a single file
+ kXMP_SonyHDVFile = 0x53484456UL,
+ /// Public file format constant: 'CNXF', a collection not really a single file
+ kXMP_CanonXFFile = 0x434E5846UL,
+
+ /// Public file format constant: 'HTML'
+ kXMP_HTMLFile = 0x48544D4CUL,
+ /// Public file format constant: 'XML '
+ kXMP_XMLFile = 0x584D4C20UL,
+ /// Public file format constant: 'text'
+ kXMP_TextFile = 0x74657874UL,
+
+ // -------------------------------
+ // Adobe application file formats.
+
+ /// Adobe application file format constant: 'PSD '
+ kXMP_PhotoshopFile = 0x50534420UL,
+ /// Adobe application file format constant: 'AI '
+ kXMP_IllustratorFile = 0x41492020UL,
+ /// Adobe application file format constant: 'INDD'
+ kXMP_InDesignFile = 0x494E4444UL,
+ /// Adobe application file format constant: 'AEP '
+ kXMP_AEProjectFile = 0x41455020UL,
+ /// Adobe application file format constant: 'AET ', After Effects Project Template
+ kXMP_AEProjTemplateFile = 0x41455420UL,
+ /// Adobe application file format constant: 'FFX '
+ kXMP_AEFilterPresetFile = 0x46465820UL,
+ /// Adobe application file format constant: 'NCOR'
+ kXMP_EncoreProjectFile = 0x4E434F52UL,
+ /// Adobe application file format constant: 'PRPJ'
+ kXMP_PremiereProjectFile = 0x5052504AUL,
+ /// Adobe application file format constant: 'PRTL'
+ kXMP_PremiereTitleFile = 0x5052544CUL,
+ /// Adobe application file format constant: 'UCF ', Universal Container Format
+ kXMP_UCFFile = 0x55434620UL,
+
+ // -------
+ // Others.
+
+ /// Unknown file format constant: ' '
+ kXMP_UnknownFile = 0x20202020UL
+
+};
+
+/// Type for file format identification constants. See \c #kXMP_PDFFile and following.
+typedef XMP_Uns32 XMP_FileFormat;
+
+// -------------------------------------------------------------------------------------------------
+
+/// Byte-order masks, do not use directly
+enum {
+ kXMP_CharLittleEndianMask = 1,
+ kXMP_Char16BitMask = 2,
+ kXMP_Char32BitMask = 4
+};
+
+/// Constants to allow easy testing for 16/32 bit and big/little endian.
+enum {
+ /// 8-bit
+ kXMP_Char8Bit = 0,
+ /// 16-bit big-endian
+ kXMP_Char16BitBig = kXMP_Char16BitMask,
+ /// 16-bit little-endian
+ kXMP_Char16BitLittle = kXMP_Char16BitMask | kXMP_CharLittleEndianMask,
+ /// 32-bit big-endian
+ kXMP_Char32BitBig = kXMP_Char32BitMask,
+ /// 32-bit little-endian
+ kXMP_Char32BitLittle = kXMP_Char32BitMask | kXMP_CharLittleEndianMask,
+ /// Variable or not-yet-known cases
+ kXMP_CharUnknown = 1
+};
+
+/// \name Macros to test components of the character form mask
+/// @{
+///
+/// \def XMP_CharFormIs16Bit
+/// \brief Macro reports the encoding of a character.
+/// \param f The character to check.
+///
+/// \def XMP_CharFormIs32Bit
+/// \brief Macro reports the encoding of a character.
+/// \param f The character to check.
+///
+/// \def XMP_CharFormIsBigEndian
+/// \brief Macro reports the byte-order of a character.
+/// \param f The character to check.
+///
+/// \def XMP_CharFormIsLittleEndian
+/// \brief Macro reports the byte-order of a character.
+/// \param f The character to check.
+///
+/// \def XMP_GetCharSize
+/// \brief Macro reports the byte-size of a character.
+/// \param f The character to check.
+///
+/// \def XMP_CharToSerializeForm
+/// \brief Macro converts \c XMP_Uns8 to \c XMP_OptionBits.
+/// \param cf The character to convert.
+///
+/// \def XMP_CharFromSerializeForm
+/// \brief Macro converts \c XMP_OptionBits to \c XMP_Uns8.
+/// \param sf The character to convert.
+///
+/// @}
+
+#define XMP_CharFormIs16Bit(f) ( ((int)(f) & kXMP_Char16BitMask) != 0 )
+#define XMP_CharFormIs32Bit(f) ( ((int)(f) & kXMP_Char32BitMask) != 0 )
+#define XMP_CharFormIsBigEndian(f) ( ((int)(f) & kXMP_CharLittleEndianMask) == 0 )
+#define XMP_CharFormIsLittleEndian(f) ( ((int)(f) & kXMP_CharLittleEndianMask) != 0 )
+#define XMP_GetCharSize(f) ( ((int)(f)&6) == 0 ? 1 : (int)(f)&6 )
+#define XMP_CharToSerializeForm(cf) ( (XMP_OptionBits)(cf) )
+#define XMP_CharFromSerializeForm(sf) ( (XMP_Uns8)(sf) )
+
+/// \def kXMPFiles_UnknownOffset
+/// \brief Constant for an unknown packet offset within a file.
+#define kXMPFiles_UnknownOffset ((XMP_Int64)-1)
+
+/// \def kXMPFiles_UnknownLength
+/// \brief Constant for an unknown packet length within a file.
+#define kXMPFiles_UnknownLength ((XMP_Int32)-1)
+
+/// XMP packet description
+struct XMP_PacketInfo {
+
+ /// Packet offset in the file in bytes, -1 if unknown.
+ XMP_Int64 offset;
+ /// Packet length in the file in bytes, -1 if unknown.
+ XMP_Int32 length;
+ /// Packet padding size in bytes, zero if unknown.
+ XMP_Int32 padSize; // Zero if unknown.
+
+ /// Character format using the values \c kXMP_Char8Bit, \c kXMP_Char16BitBig, etc.
+ XMP_Uns8 charForm;
+ /// True if there is a packet wrapper and the trailer says writeable by dumb packet scanners.
+ XMP_Bool writeable;
+ /// True if there is a packet wrapper, the "<?xpacket...>" XML processing instructions.
+ XMP_Bool hasWrapper;
+
+ /// Padding to make the struct's size be a multiple 4.
+ XMP_Uns8 pad;
+
+ /// Default constructor.
+ XMP_PacketInfo() : offset(kXMPFiles_UnknownOffset), length(kXMPFiles_UnknownLength),
+ padSize(0), charForm(0), writeable(0), hasWrapper(0), pad(0) {};
+
+};
+
+/// Version of the XMP_PacketInfo type
+enum {
+ /// Version of the XMP_PacketInfo type
+ kXMP_PacketInfoVersion = 3
+};
+
+// -------------------------------------------------------------------------------------------------
+
+/// Option bit flags for \c TXMPFiles::Initialize().
+enum {
+ /// Ignore non-XMP text that uses an undefined "local" encoding.
+ kXMPFiles_IgnoreLocalText = 0x0002,
+ /// Combination of flags necessary for server products using XMPFiles.
+ kXMPFiles_ServerMode = kXMPFiles_IgnoreLocalText
+};
+
+/// Option bit flags for \c TXMPFiles::GetFormatInfo().
+enum {
+
+ /// Can inject first-time XMP into an existing file.
+ kXMPFiles_CanInjectXMP = 0x00000001,
+
+ /// Can expand XMP or other metadata in an existing file.
+ kXMPFiles_CanExpand = 0x00000002,
+
+ /// Can copy one file to another, writing new metadata.
+ kXMPFiles_CanRewrite = 0x00000004,
+
+ /// Can expand, but prefers in-place update.
+ kXMPFiles_PrefersInPlace = 0x00000008,
+
+ /// Supports reconciliation between XMP and other forms.
+ kXMPFiles_CanReconcile = 0x00000010,
+
+ /// Allows access to just the XMP, ignoring other forms.
+ kXMPFiles_AllowsOnlyXMP = 0x00000020,
+
+ /// File handler returns raw XMP packet information.
+ kXMPFiles_ReturnsRawPacket = 0x00000040,
+
+ /// The file handler does the file open and close.
+ kXMPFiles_HandlerOwnsFile = 0x00000100,
+
+ /// The file handler allows crash-safe file updates.
+ kXMPFiles_AllowsSafeUpdate = 0x00000200,
+
+ /// The file format needs the XMP packet to be read-only.
+ kXMPFiles_NeedsReadOnlyPacket = 0x00000400,
+
+ /// The file handler uses a "sidecar" file for the XMP.
+ kXMPFiles_UsesSidecarXMP = 0x00000800,
+
+ /// The format is folder oriented, for example the P2 video format.
+ kXMPFiles_FolderBasedFormat = 0x00001000,
+
+ /// The file Handler is capable of notifying progress notifications
+ kXMPFiles_CanNotifyProgress = 0x00002000,
+
+ /// The plugin handler is not capable for delay loading
+ kXMPFiles_NeedsPreloading = 0x00004000
+
+};
+
+/// Option bit flags for \c TXMPFiles::OpenFile().
+enum {
+
+ /// Open for read-only access.
+ kXMPFiles_OpenForRead = 0x00000001,
+
+ /// Open for reading and writing.
+ kXMPFiles_OpenForUpdate = 0x00000002,
+
+ /// Only the XMP is wanted, allows space/time optimizations.
+ kXMPFiles_OpenOnlyXMP = 0x00000004,
+
+ /// Force use of the given handler (format), do not even verify the format.
+ kXMPFiles_ForceGivenHandler = 0x00000008,
+
+ /// Be strict about only attempting to use the designated file handler, no fallback to other handlers.
+ kXMPFiles_OpenStrictly = 0x00000010,
+
+ /// Require the use of a smart handler.
+ kXMPFiles_OpenUseSmartHandler = 0x00000020,
+
+ /// Force packet scanning, do not use a smart handler.
+ kXMPFiles_OpenUsePacketScanning = 0x00000040,
+
+ /// Only packet scan files "known" to need scanning.
+ kXMPFiles_OpenLimitedScanning = 0x00000080,
+
+ /// Attempt to repair a file opened for update, default is to not open (throw an exception).
+ kXMPFiles_OpenRepairFile = 0x00000100,
+
+ /// When updating a file, spend the effort necessary to optimize file layout.
+ kXMPFiles_OptimizeFileLayout = 0x00000200
+
+};
+
+/// Option bit flags for \c TXMPFiles::CloseFile().
+enum {
+ /// Write into a temporary file and swap for crash safety.
+ kXMPFiles_UpdateSafely = 0x0001
+};
+
+// =================================================================================================
+// Error notification and Exceptions
+// =================================
+
+/// \name Error notification and Exceptions
+/// @{
+///
+/// From the beginning through version 5.5, XMP Tookit errors result in throwing an \c XMP_Error
+/// exception. For the most part exceptions were thrown early and thus API calls aborted as soon as
+/// an error was detected. Starting in version 5.5, support has been added for notifications of
+/// errors arising in calls to \c TXMPMeta and \c TXMPFiles functions.
+///
+/// A client can register an error notification callback function for a \c TXMPMeta or \c TXMPFiles
+/// object. This can be done as a global default or individually to each object. The global default
+/// applies to all objects created after it is registered. Within the object there is no difference
+/// between the global default or explicitly registered callback. The callback function returns a
+/// \c bool value indicating if recovery should be attempted (true) or an exception thrown (false).
+/// If no callback is registered, a best effort at recovery and continuation will be made with an
+/// exception thrown if recovery is not possible. More details can be found in the \c TXMPMeta and
+/// \c TXMPFiles documentation.
+///
+/// The \c XMP_Error class contains a numeric code and an English explanation. New numeric codes may
+/// be added at any time. There are typically many possible explanations for each numeric code. The
+/// explanations try to be precise about the specific circumstances causing the error.
+///
+/// \note The explanation string is for debugging use only. It must not be shown to users in a
+/// final product. It is written for developers not users, and never localized.
+
+typedef XMP_Uns8 XMP_ErrorSeverity;
+
+/// Severity codes for error notifications
+enum {
+ /// Partial recovery and continuation is possible.
+ kXMPErrSev_Recoverable = 0,
+ /// Recovery is not possible, an exception will be thrown aborting the API call.
+ kXMPErrSev_OperationFatal = 1,
+ /// Recovery is not possible, an exception will be thrown, the file is corrupt and possibly unusable.
+ kXMPErrSev_FileFatal = 2,
+ /// Recovery is not possible, an exception will be thrown, the entire process should be aborted.
+ kXMPErrSev_ProcessFatal = 3
+};
+
+// -------------------------------------------------------------------------------------------------
+/// The signature of a client-defined callback for TXMPMeta error notifications.
+///
+/// @param context A pointer used to carry client-private context.
+///
+/// @param severity The severity of the error, see the \c XMP_ErrorSeverity values.
+///
+/// @param cause A numeric code for the cause of the error, from the XMP_Error exception codes.
+/// Codes used with TXMPMeta error notifications:
+/// \li \c kXMPErr_BadXML - An XML syntax error found during parsing.
+/// \li \c kXMPErr_BadRDF - A syntax or semantic parsing error in the XMP subset of RDF.
+/// \li \c kXMPErr_BadXMP - A semantic XMP data model error.
+/// \li \c kXMPErr_BadValue - An XMP value error, wrong type, out of range, etc.
+/// \li \c kXMPErr_NoMemory - A heap allocation failure.
+///
+/// @param message An explanation of the error, for debugging use only. This should not be displayed
+/// to users in a final product.
+///
+/// @return True if the operation should continue with a best effort attempt at recovery, false if
+/// it should be aborted with an exception thrown from the library back to the original caller.
+/// Recovery is possible only if the severity is kXMPErrSev_Recoverable, an exception will be
+/// thrown on return from the callback in all other cases.
+///
+/// @see \c TXMPMeta::SetDefaultErrorCallback() and \c TXMPMeta::SetErrorCallback()
+
+typedef bool (* XMPMeta_ErrorCallbackProc) ( void* context, XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr message );
+
+// -------------------------------------------------------------------------------------------------
+/// The signature of a client-defined callback for TXMPFiles error notifications.
+///
+/// @param context A pointer used to carry client-private context.
+///
+/// @param filePath The path for the file involved in the error.
+///
+/// @param severity The severity of the error, see the \c XMP_ErrorSeverity values.
+///
+/// @param cause A numeric code for the cause of the error, from the XMP_Error exception codes.
+/// Codes used with TXMPFiles error notifications:
+/// \li \c kXMPErr_NoFile - A file does not exist
+/// \li \c kXMPErr_FilePermission - A file exists but cannot be opened
+/// \li \c kXMPErr_FilePathNotAFile - A path exists which is not a file
+/// \li \c dXMPErr_RejectedFileExtension - Any Operation called on rejected file extension
+/// \li \c KXMPErr_NoFileHandler - No suitable handler is found for the file
+/// \li \c kXMPErr_DiskSpace - A file write fails due to lack of disk space
+/// \li \c kXMPErr_ReadError - A file read fails
+/// \li \c kXMPErr_WriteError - A file write fails for some other reason than space
+/// \li \c kXMPErr_BadFileFormat - A file is corrupt or ill-formed
+/// \li \c kXMPErr_BadBlockFormat - A portion of a file is corrupt or ill-formed
+/// \li \c kXMPErr_BadValue - An XMP or non-XMP metadata item has an invalid value
+/// \li \c kXMPErr_NoMemory - A heap allocation failure
+///
+/// @param message An explanation of the error, for debugging use only. This should not be displayed
+/// to users in a final product.
+///
+/// @return True if the operation should continue with a best effort attempt at recovery, false if
+/// it should be aborted with an exception thrown from the library back to the original caller.
+/// Recovery is possible only if the severity is kXMPErrSev_Recoverable, an exception will be
+/// thrown on return from the callback in all other cases.
+///
+/// @see \c TXMPFiles::SetDefaultErrorCallback() and \c TXMPFiles::SetErrorCallback()
+
+typedef bool (* XMPFiles_ErrorCallbackProc) ( void* context, XMP_StringPtr filePath, XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr message );
+
+// -------------------------------------------------------------------------------------------------
+/// Internal: The signatures of client-side wrappers for the error notification callbacks.
+
+typedef XMP_Bool (* XMPMeta_ErrorCallbackWrapper) ( XMPMeta_ErrorCallbackProc clientProc, void* context,
+ XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr message );
+
+typedef XMP_Bool (* XMPFiles_ErrorCallbackWrapper) ( XMPFiles_ErrorCallbackProc clientProc, void* context,
+ XMP_StringPtr filePath, XMP_ErrorSeverity severity,
+ XMP_Int32 cause, XMP_StringPtr message );
+
+/// XMP Toolkit error, associates an error code with a descriptive error string.
+class XMP_Error {
+public:
+
+ /// @brief Constructor for an XMP_Error.
+ ///
+ /// @param _id The numeric code.
+ ///
+ /// @param _errMsg The descriptive string, for debugging use only. It must not be shown to users
+ /// in a final product. It is written for developers, not users, and never localized.
+ XMP_Error ( XMP_Int32 _id, XMP_StringPtr _errMsg ) : id(_id), errMsg(_errMsg), notified(false) {};
+
+ /// Retrieves the numeric code from an XMP_Error.
+ inline XMP_Int32 GetID() const { return id; };
+
+ /// Retrieves the descriptive string from an XMP_Error.
+ inline XMP_StringPtr GetErrMsg() const { return errMsg; };
+
+ /// Retrieves the information whether particular error is notified or not
+ inline XMP_Bool IsNotified() const { return notified; }
+
+ /// Sets the notification status for an error
+ inline void SetNotified() { notified = true; };
+
+private:
+ /// Exception code. See constants \c #kXMPErr_Unknown and following.
+ XMP_Int32 id;
+ /// Descriptive string, for debugging use only. It must not be shown to users in a final
+ /// product. It is written for developers, not users, and never localized.
+ XMP_StringPtr errMsg;
+ /// Variable to store whether this particular error is notified to user or not
+ XMP_Bool notified;
+};
+
+/// XMP_Error exception code constants
+enum {
+
+ // --------------------
+ /// Generic error codes.
+
+ /// No error
+ kXMPErr_NoError = -1,
+
+ /// Generic unknown error
+ kXMPErr_Unknown = 0,
+ /// Generic undefined error
+ kXMPErr_TBD = 1,
+ /// Generic unavailable error
+ kXMPErr_Unavailable = 2,
+ /// Generic bad object error
+ kXMPErr_BadObject = 3,
+ /// Generic bad parameter error
+ kXMPErr_BadParam = 4,
+ /// Generic bad value error
+ kXMPErr_BadValue = 5,
+ /// Generic assertion failure
+ kXMPErr_AssertFailure = 6,
+ /// Generic enforcement failure
+ kXMPErr_EnforceFailure = 7,
+ /// Generic unimplemented error
+ kXMPErr_Unimplemented = 8,
+ /// Generic internal failure
+ kXMPErr_InternalFailure = 9,
+ /// Generic deprecated error
+ kXMPErr_Deprecated = 10,
+ /// Generic external failure
+ kXMPErr_ExternalFailure = 11,
+ /// Generic user abort error
+ kXMPErr_UserAbort = 12,
+ /// Generic standard exception
+ kXMPErr_StdException = 13,
+ /// Generic unknown exception
+ kXMPErr_UnknownException = 14,
+ /// Generic out-of-memory error
+ kXMPErr_NoMemory = 15,
+ /// Progress reporting callback requested abort
+ kXMPErr_ProgressAbort = 16,
+
+ // ------------------------------------
+ // More specific parameter error codes.
+
+ /// Bad schema parameter
+ kXMPErr_BadSchema = 101,
+ /// Bad XPath parameter
+ kXMPErr_BadXPath = 102,
+ /// Bad options parameter
+ kXMPErr_BadOptions = 103,
+ /// Bad index parameter
+ kXMPErr_BadIndex = 104,
+ /// Bad iteration position
+ kXMPErr_BadIterPosition = 105,
+ /// XML parsing error (deprecated)
+ kXMPErr_BadParse = 106,
+ /// Serialization error
+ kXMPErr_BadSerialize = 107,
+ /// File format error
+ kXMPErr_BadFileFormat = 108,
+ /// No file handler found for format
+ kXMPErr_NoFileHandler = 109,
+ /// Data too large for JPEG file format
+ kXMPErr_TooLargeForJPEG = 110,
+ /// A file does not exist
+ kXMPErr_NoFile = 111,
+ /// A file exists but cannot be opened
+ kXMPErr_FilePermission = 112,
+ /// A file write failed due to lack of disk space
+ kXMPErr_DiskSpace = 113,
+ /// A file read failed
+ kXMPErr_ReadError = 114,
+ /// A file write failed for a reason other than lack of disk space
+ kXMPErr_WriteError = 115,
+ /// A block of a file is ill-formed, e.g. invalid IPTC-IIM in a photo
+ kXMPErr_BadBlockFormat = 116,
+ /// File Path is not a file
+ kXMPErr_FilePathNotAFile = 117,
+ /// Rejected File extension
+ kXMPErr_RejectedFileExtension = 118,
+
+ // -----------------------------------------------
+ // File format and internal structure error codes.
+
+ /// XML format error
+ kXMPErr_BadXML = 201,
+ /// RDF format error
+ kXMPErr_BadRDF = 202,
+ /// XMP format error
+ kXMPErr_BadXMP = 203,
+ /// Empty iterator
+ kXMPErr_EmptyIterator = 204,
+ /// Unicode error
+ kXMPErr_BadUnicode = 205,
+ /// TIFF format error
+ kXMPErr_BadTIFF = 206,
+ /// JPEG format error
+ kXMPErr_BadJPEG = 207,
+ /// PSD format error
+ kXMPErr_BadPSD = 208,
+ /// PSIR format error
+ kXMPErr_BadPSIR = 209,
+ /// IPTC format error
+ kXMPErr_BadIPTC = 210,
+ /// MPEG format error
+ kXMPErr_BadMPEG = 211
+
+};
+
+/// @}
+
+// =================================================================================================
+// Client callbacks
+// ================
+
+// -------------------------------------------------------------------------------------------------
+/// \name Special purpose callback functions
+/// @{
+
+/// A signed 32-bit integer used as a status result for the output callback routine,
+/// \c XMP_TextOutputProc. Zero means no error, all other values except -1 are private to the callback.
+/// The callback is wrapped to prevent exceptions being thrown across DLL boundaries. Any exceptions
+/// thrown out of the callback cause a return status of -1.
+
+typedef XMP_Int32 XMP_Status;
+
+// -------------------------------------------------------------------------------------------------
+/// The signature of a client-defined callback for text output from XMP Toolkit debugging
+/// operations. The callback is invoked one or more times for each line of output. The end of a line
+/// is signaled by a '\\n' character at the end of the buffer. Formatting newlines are never present
+/// in the middle of a buffer, but values of properties might contain any UTF-8 characters.
+///
+/// @param refCon A pointer to client-defined data passed to the TextOutputProc.
+///
+/// @param buffer A string containing one line of output.
+///
+/// @param bufferSize The number of characters in the output buffer.
+///
+/// @return A success/fail status value. Any failure result aborts the output.
+///
+/// @see \c TXMPMeta::DumpObject()
+
+typedef XMP_Status (* XMP_TextOutputProc) ( void * refCon,
+ XMP_StringPtr buffer,
+ XMP_StringLen bufferSize );
+
+// -------------------------------------------------------------------------------------------------
+/// The signature of a client-defined callback to check for a user request to abort a time-consuming
+/// operation within XMPFiles.
+///
+/// @param arg A pointer to caller-defined data passed from the registration call.
+///
+/// @return True to abort the current operation, which results in an exception being thrown.
+///
+/// @see \c TXMPFiles::SetAbortProc()
+
+typedef bool (* XMP_AbortProc) ( void * arg );
+
+// -------------------------------------------------------------------------------------------------
+/// The signature of a client-defined callback for progress report notifications.
+///
+/// @param context A pointer used to carry client-private context.
+///
+/// @param elapsedTime The time in seconds since the progress reporting started.
+///
+/// @param fractionDone A float value estimating the amount of work already done, in the range of
+/// 0.0 to 1.0. A value of 0.0 is given if the amount is not known, this happens if there is no
+/// estimate total for the total work. The units of work are not defined, but should usually be
+/// related to the number of bytes of I/O. This will go backwards if total work estimate changes.
+///
+/// @param secondsToGo A float value estimating the number of seconds left to complete the file
+/// operation. A value of 0.0 is given if the amount is not known, this happens if the amount of
+/// total work is unknown. This can go backwards according to throughput or if work estimate changes.
+///
+/// @return True if the file operation should continue, false if it should be aborted with an
+/// exception being thrown from the XMPFiles library back to the original caller.
+///
+/// @see \c TXMPFiles::SetDefaultProgressCallback() and \c TXMPFiles::SetProgressCallback()
+
+typedef bool (* XMP_ProgressReportProc) ( void * context, float elapsedTime, float fractionDone, float secondsToGo );
+
+// -------------------------------------------------------------------------------------------------
+/// Internal: The signature of a client-side wrapper for the progress report callback.
+
+typedef XMP_Bool (* XMP_ProgressReportWrapper) ( XMP_ProgressReportProc proc, void * context,
+ float elapsedTime, float fractionDone, float secondsToGo );
+
+/// @}
+
+// =================================================================================================
+// Stuff with no better place to be
+// ================================
+
+/// XMP Toolkit version information
+typedef struct XMP_VersionInfo {
+ /// The primary release number, the "1" in version "1.2.3".
+ XMP_Uns8 major;
+ /// The secondary release number, the "2" in version "1.2.3".
+ XMP_Uns8 minor;
+ /// The tertiary release number, the "3" in version "1.2.3".
+ XMP_Uns8 micro;
+ /// A 0/1 boolean value, true if this is a debug build.
+ XMP_Bool isDebug;
+ /// A rolling build number, monotonically increasing in a release.
+ XMP_Uns32 build;
+ /// Individual feature implementation flags.
+ XMP_Uns32 flags;
+ /// A comprehensive version information string.
+ XMP_StringPtr message;
+} XMP_VersionInfo;
+
+// =================================================================================================
+
+#if __cplusplus
+} // extern "C"
+#endif
+
+#include <vector>
+
+#endif // __XMP_Const_h__
diff --git a/gpr/source/lib/xmp_core/public/include/XMP_Environment.h b/gpr/source/lib/xmp_core/public/include/XMP_Environment.h
new file mode 100644
index 0000000..fd459ad
--- /dev/null
+++ b/gpr/source/lib/xmp_core/public/include/XMP_Environment.h
@@ -0,0 +1,165 @@
+#ifndef __XMP_Environment_h__
+#define __XMP_Environment_h__ 1
+
+// =================================================================================================
+// XMP_Environment.h - Build environment flags for the XMP toolkit.
+// ================================================================
+//
+// This header is just C preprocessor macro definitions to set up the XMP toolkit build environment.
+// It must be the first #include in any chain since it might affect things in other #includes.
+//
+// =================================================================================================
+
+// =================================================================================================
+// Copyright 2002 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.
+// =================================================================================================
+
+// =================================================================================================
+// Determine the Platform
+// =================================================================================================
+
+#ifdef _WIN32
+
+ #define XMP_MacBuild 0
+ #define XMP_iOSBuild 0
+ #define XMP_WinBuild 1
+ #define XMP_UNIXBuild 0
+
+#elif __APPLE__
+
+ #include "TargetConditionals.h"
+
+ #if TARGET_OS_IPHONE
+
+ #define XMP_MacBuild 0
+ #define XMP_iOSBuild 1
+ #define XMP_WinBuild 0
+ #define XMP_UNIXBuild 0
+
+ #else
+
+ #define XMP_MacBuild 1
+ #define XMP_iOSBuild 0
+ #define XMP_WinBuild 0
+ #define XMP_UNIXBuild 0
+
+ #endif
+
+#elif __ANDROID__
+
+#elif __linux__ || __unix__
+
+ #define XMP_MacBuild 0
+ #define XMP_WinBuild 0
+ #define XMP_UNIXBuild 1
+ #define XMP_iOSBuild 0
+
+#else
+ #error "XMP environment error - Unknown compiler"
+#endif
+
+// =================================================================================================
+// Common Macros
+// =============
+
+#if defined ( DEBUG )
+ #if defined ( NDEBUG )
+ #error "XMP environment error - both DEBUG and NDEBUG are defined"
+ #endif
+ #define XMP_DebugBuild 1
+#endif
+
+#if defined ( NDEBUG )
+ #define XMP_DebugBuild 0
+#endif
+
+#ifndef XMP_DebugBuild
+ #define XMP_DebugBuild 0
+#endif
+
+#if XMP_DebugBuild
+ #include <stdio.h> // The assert macro needs printf.
+#endif
+
+#ifndef DISABLE_SERIALIZED_IMPORT_EXPORT
+ #define DISABLE_SERIALIZED_IMPORT_EXPORT 0
+#endif
+
+#ifndef XMP_64
+ #if _WIN64 || defined(_LP64)
+ #define XMP_64 1
+ #else
+ #define XMP_64 0
+ #endif
+#endif
+
+// =================================================================================================
+// Macintosh Specific Settings
+// ===========================
+#if (XMP_MacBuild)
+ #define XMP_HELPER_DLL_IMPORT __attribute__((visibility("default")))
+ #define XMP_HELPER_DLL_EXPORT __attribute__((visibility("default")))
+ #define XMP_HELPER_DLL_PRIVATE __attribute__((visibility("hidden")))
+#endif
+
+// =================================================================================================
+// Windows Specific Settings
+// =========================
+#if (XMP_WinBuild)
+ #define XMP_HELPER_DLL_IMPORT
+ #define XMP_HELPER_DLL_EXPORT
+ #define XMP_HELPER_DLL_PRIVATE
+#endif
+
+// =================================================================================================
+// UNIX Specific Settings
+// ======================
+#if (XMP_UNIXBuild)
+ #define XMP_HELPER_DLL_IMPORT
+ #define XMP_HELPER_DLL_EXPORT
+ #define XMP_HELPER_DLL_PRIVATE
+#endif
+
+// =================================================================================================
+// IOS Specific Settings
+// ===========================
+#if (XMP_iOSBuild)
+ #include <TargetConditionals.h>
+ #if (TARGET_CPU_ARM)
+ #define XMP_IOS_ARM 1
+ #else
+ #define XMP_IOS_ARM 0
+ #endif
+ #define XMP_HELPER_DLL_IMPORT __attribute__((visibility("default")))
+ #define XMP_HELPER_DLL_EXPORT __attribute__((visibility("default")))
+ #define XMP_HELPER_DLL_PRIVATE __attribute__((visibility("hidden")))
+#endif
+
+// =================================================================================================
+// Banzai Specific Settings
+// ======================
+#if (XMP_Banzai)
+ #define XMP_HELPER_DLL_IMPORT
+ #define XMP_HELPER_DLL_EXPORT
+ #define XMP_HELPER_DLL_PRIVATE
+#endif
+
+
+// =================================================================================================
+
+#if (XMP_DynamicBuild)
+ #define XMP_PUBLIC XMP_HELPER_DLL_EXPORT
+ #define XMP_PRIVATE XMP_HELPER_DLL_PRIVATE
+#elif (XMP_StaticBuild)
+ #define XMP_PUBLIC
+ #define XMP_PRIVATE
+#else
+ #define XMP_PUBLIC XMP_HELPER_DLL_IMPORT
+ #define XMP_PRIVATE XMP_HELPER_DLL_PRIVATE
+#endif
+
+#endif // __XMP_Environment_h__
diff --git a/gpr/source/lib/xmp_core/public/include/XMP_IO.hpp b/gpr/source/lib/xmp_core/public/include/XMP_IO.hpp
new file mode 100644
index 0000000..d485392
--- /dev/null
+++ b/gpr/source/lib/xmp_core/public/include/XMP_IO.hpp
@@ -0,0 +1,171 @@
+#ifndef __XMP_IO_hpp__
+#define __XMP_IO_hpp__ 1
+
+// =================================================================================================
+// ADOBE SYSTEMS INCORPORATED
+// Copyright 2010 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.
+// =================================================================================================
+
+#include "XMP_Environment.h" // ! XMP_Environment.h must be the first included header.
+
+#include "XMP_Const.h"
+
+// =================================================================================================
+/// \class XMP_IO XMP_IO.hpp
+/// \brief Abstract base class for client-managed I/O with \c TXMPFiles.
+///
+/// \c XMP_IO is an abstract base class for client-managed I/O with \c TXMPFiles. This allows a
+/// client to use the embedded metadata processing logic of \c TXMPFiles in cases where a string
+/// file path cannot be provided, or where it is impractical to allow \c TXMPFiles to separately
+/// open the file and do its own I/O. Although described in terms of files, any form of storage may
+/// be used as long as the functions operate as defined.
+///
+/// This is not a general purpose I/O class. It contains only the necessary functions needed by the
+/// internals of \c TXMPFiles. It is intended to be used as an adaptor for an existing I/O mechanism
+/// that the client wants \c TXMPFiles to use.
+///
+/// To use \c XMP_IO, a client creates a derived class then uses the form of \c TCMPFiles::OpenFile
+/// that takes an \c XMP_IO parameter instead of a string file path. The derived \c XMP_IO object
+/// must be ready for use when \c TCMPFiles::OpenFile is called.
+///
+/// There are no Open or Close functions in \c XMP_IO, they are specific to each implementation. The
+/// derived \c XMP_IO object must be open and ready for use before being passed to \c
+/// TXMP_Files::OpenFile, and remain open and ready for use until \c TXMP_Files::CloseFile returns,
+/// or some other fatal error occurs. The client has final responsibility for closing and
+/// terminating the derived \c XMP_IO object.
+// =================================================================================================
+
+class XMP_IO {
+public:
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Read into a buffer, returning the number of bytes read.
+ ///
+ /// Read into a buffer, returning the number of bytes read. Returns the actual number of bytes
+ /// read. Throws an exception if requireSuccess is true and not enough data is available.
+ /// Throwing \c XMPError is recommended. The buffer content and I/O position after a throw are
+ /// undefined.
+ ///
+ /// @param buffer A pointer to the buffer.
+ /// @param count The length of the buffer in bytes.
+ /// @param readAll True if reading less than the requested amount is a failure.
+ ///
+ /// @return Returns the number of bytes read.
+
+ enum { kReadAll = true };
+
+ virtual XMP_Uns32 Read ( void* buffer, XMP_Uns32 count, bool readAll = false ) = 0;
+
+ inline XMP_Uns32 ReadAll ( void* buffer, XMP_Uns32 bytes )
+ { return this->Read ( buffer, bytes, kReadAll ); };
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Write from a buffer.
+ ///
+ /// Write from a buffer, overwriting existing data and extending the file as necesary. All data
+ /// must be written or an exception thrown. Throwing \c XMPError is recommended.
+ ///
+ /// @param buffer A pointer to the buffer.
+ /// @param count The length of the buffer in bytes.
+
+ virtual void Write ( const void* buffer, XMP_Uns32 count ) = 0;
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Set the I/O position, returning the new absolute offset in bytes.
+ ///
+ /// Set the I/O position, returning the new absolute offset in bytes. The offset parameter may
+ /// be positive or negative. A seek beyond EOF is allowed when writing and extends the file, it
+ /// is equivalent to seeking to EOF then writing the needed amount of undefined data. A
+ /// read-only seek beyond EOF throws an exception. Throwing \c XMPError is recommended.
+ ///
+ /// @param offset The offset relative to the mode.
+ /// @param mode The mode, or origin, of the seek.
+ ///
+ /// @return The new absolute offset in bytes.
+
+ virtual XMP_Int64 Seek ( XMP_Int64 offset, SeekMode mode ) = 0;
+
+ inline XMP_Int64 Offset() { return this->Seek ( 0, kXMP_SeekFromCurrent ); };
+ inline XMP_Int64 Rewind() { return this->Seek ( 0, kXMP_SeekFromStart ); }; // Always returns 0.
+ inline XMP_Int64 ToEOF() { return this->Seek ( 0, kXMP_SeekFromEnd ); };
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Return the length of the file in bytes.
+ ///
+ /// Return the length of the file in bytes. The I/O position is unchanged.
+ ///
+ /// @return The length of the file in bytes.
+
+ virtual XMP_Int64 Length() = 0;
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Truncate the file to the given length.
+ ///
+ /// Truncate the file to the given length. The I/O position after truncation is unchanged if
+ /// still valid, otherwise it is set to the new EOF. Throws an exception if the new length is
+ /// longer than the file's current length. Throwing \c XMPError is recommended.
+ ///
+ /// @param length The new length for the file, must be less than or equal to the current length.
+
+ virtual void Truncate ( XMP_Int64 length ) = 0;
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Create an associated temp file for use in a safe-save style operation.
+ ///
+ /// Create an associated temp file, for example in the same directory and with a related name.
+ /// Returns an already existing temp with no other action. The temp must be opened for
+ /// read-write access. It will be used in a safe-save style operation, using some of the
+ /// original file plus new portions to write the temp, then replacing the original from the temp
+ /// when done. Throws an exception if the owning object is opened for read-only access, or if
+ /// the temp file cannot be created. Throwing \c XMPError is recommended.
+ ///
+ /// The temp file is normally closed and deleted, and the temporary \c XMP_IO object deleted, by
+ /// a call to \c AbsorbTemp or \c DeleteTemp. It must be closed and deleted by the derived \c
+ /// XMP_IO object's destructor if necessary.
+ ///
+ /// \c DeriveTemp may be called on a temporary \c XMP_IO object.
+ ///
+ /// @return A pointer to the associated temporary \c XMP_IO object.
+
+ virtual XMP_IO* DeriveTemp() = 0;
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Replace the owning file's content with that of the temp.
+ ///
+ /// Used at the end of a safe-save style operation to replace the original content with that
+ /// from the associated temp file. The temp file must be closed and deleted after the content
+ /// swap. The temporary \c XMP_IO object is deleted. Throws an exception if the temp file cannot
+ /// be absorbed. Throwing \c XMPError is recommended.
+
+ virtual void AbsorbTemp() = 0;
+
+ // ---------------------------------------------------------------------------------------------
+ /// @brief Delete a temp file, leaving the original alone.
+ ///
+ /// Used for a failed safe-save style operation. The temp file is closed and deleted without
+ /// being absorbed, and the temporary \c XMP_IO object is deleted. Does nothing if no temp
+ /// exists.
+
+ virtual void DeleteTemp() = 0;
+
+ // ---------------------------------------------------------------------------------------------
+
+ XMP_IO() {};
+ virtual ~XMP_IO() {};
+
+private:
+
+ // ---------------------------------------------------------------------------------------------
+ /// Copy construction and assignment are not public. That would require the implementation to
+ /// share state across multiple XMP_IO objects.
+
+ XMP_IO ( const XMP_IO & original );
+ void operator= ( const XMP_IO& in ) { *this = in; /* Avoid Win compile warnings. */ };
+
+};
+
+#endif // __XMP_IO_hpp__
diff --git a/gpr/source/lib/xmp_core/public/include/XMP_Version.h b/gpr/source/lib/xmp_core/public/include/XMP_Version.h
new file mode 100644
index 0000000..53707b1
--- /dev/null
+++ b/gpr/source/lib/xmp_core/public/include/XMP_Version.h
@@ -0,0 +1,52 @@
+#ifndef __XMP_Version_h__
+#define __XMP_Version_h__ 1
+
+/* --------------------------------------------------------------------------------------------- */
+/* ** IMPORTANT ** This file must be usable by strict ANSI C compilers. No "//" comments, etc. */
+/* --------------------------------------------------------------------------------------------- */
+
+/*
+// =================================================================================================
+// Copyright 2002 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.
+// =================================================================================================
+*/
+
+/* ============================================================================================= */
+/**
+XMP Toolkit Version Information
+
+Version information for the XMP toolkit is stored in the executable and available through a runtime
+call, <tt>SXMPMeta::GetVersionInfo</tt>. In addition a static version number is defined in this
+header. The information in the executable or returned by <tt>SXMPMeta::GetVersionInfo</tt> is about
+the implementation internals, it is runtime version information. The values defined in this header
+describe the version of the API used at client compile time. They do not necessarily relate to the
+runtime version.
+
+Important: Do not display the static values defined here to users as the version of XMP in use. Do
+not base runtime decisions on just this static version. It is OK to compare the static and runtime
+versions.
+
+*/
+/* ============================================================================================= */
+
+#define XMPCORE_API_VERSION_MAJOR 5
+#define XMPCORE_API_VERSION_MINOR 5
+#define XMPCORE_API_VERSION_MICRO 0
+
+#define XMPCORE_API_VERSION 5.5.0
+#define XMPCORE_API_VERSION_STRING "5.5.0"
+
+#define XMPFILES_API_VERSION_MAJOR 5
+#define XMPFILES_API_VERSION_MINOR 6
+#define XMPFILES_API_VERSION_MICRO 0
+
+#define XMPFILES_API_VERSION 5.6.0
+#define XMPFILES_API_VERSION_STRING "5.6.0"
+
+/* ============================================================================================= */
+
+#endif /* __XMP_Version_h__ */
diff --git a/gpr/source/lib/xmp_core/public/include/client-glue/TXMPFiles.incl_cpp b/gpr/source/lib/xmp_core/public/include/client-glue/TXMPFiles.incl_cpp
new file mode 100644
index 0000000..eb19887
--- /dev/null
+++ b/gpr/source/lib/xmp_core/public/include/client-glue/TXMPFiles.incl_cpp
@@ -0,0 +1,484 @@
+// =================================================================================================
+// ADOBE SYSTEMS INCORPORATED
+// Copyright 2002 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.
+// =================================================================================================
+
+// ================================================================================================
+/// \file TXMPFiles.incl_cpp
+/// \brief The implementation of the TXMPFiles template class.
+
+#if XMP_WinBuild
+ #pragma warning ( disable : 4003 ) // not enough actual parameters for macro
+ #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning)
+#endif
+
+#include "client-glue/WXMP_Common.hpp"
+
+#include "client-glue/WXMPFiles.hpp"
+
+// =================================================================================================
+// Implementation Guidelines
+// =========================
+//
+// The implementations of the template functions are very stylized. The jobs done in this code are:
+//
+// 1. ...
+//
+// =================================================================================================
+
+#ifndef XMPFiles_TraceCTorDTor
+ #define XMPFiles_TraceCTorDTor 0
+#endif
+
+#if XMPFiles_TraceCTorDTor
+ class XFPeek { // Hack to peek at the client ref count in the internal object.
+ public:
+ XFPeek();
+ virtual ~XFPeek();
+ XMP_Int32 clientRefs;
+ };
+#endif
+
+// =================================================================================================
+
+XMP_MethodIntro(TXMPFiles,void)::
+SetClientString ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen )
+{
+ tStringObj * clientStr = (tStringObj*) clientPtr;
+ clientStr->assign ( valuePtr, valueLen );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,void)::
+SetClientStringVector ( void * clientPtr, XMP_StringPtr * arrayPtr, XMP_Uns32 stringCount )
+{
+ std::vector<tStringObj>* clientVec = (std::vector<tStringObj>*) clientPtr;
+ clientVec->clear();
+ for ( XMP_Uns32 i = 0; i < stringCount; ++i ) {
+ tStringObj nextValue ( arrayPtr[i] );
+ clientVec->push_back ( nextValue );
+ }
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,void)::
+GetVersionInfo ( XMP_VersionInfo * versionInfo )
+{
+ WrapNoCheckVoid ( zXMPFiles_GetVersionInfo_1 ( versionInfo ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,bool)::
+Initialize()
+{
+ WrapCheckBool ( ok, zXMPFiles_Initialize_1 ( 0 ) );
+ return ok;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,bool)::
+Initialize( const char* pluginFolder, const char* plugins )
+{
+ WrapCheckBool ( ok, zXMPFiles_Initialize_2 ( 0, pluginFolder, plugins ) );
+ return ok;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,bool)::
+Initialize ( XMP_OptionBits options )
+{
+ WrapCheckBool ( ok, zXMPFiles_Initialize_1 ( options ) );
+ return ok;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,bool)::
+Initialize ( XMP_OptionBits options, const char* pluginFolder, const char* plugins )
+{
+ WrapCheckBool ( ok, zXMPFiles_Initialize_2 ( options, pluginFolder, plugins ) );
+ return ok;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,void)::
+Terminate()
+{
+ WrapNoCheckVoid ( zXMPFiles_Terminate_1() );
+}
+
+// =================================================================================================
+
+static XMPFilesRef Default_CTor()
+{
+ WrapCheckXMPFilesRef ( newRef, zXMPFiles_CTor_1() );
+ return newRef;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_CTorDTorIntro(TXMPFiles)::
+TXMPFiles() : xmpFilesRef(Default_CTor())
+{
+ #if XMPFiles_TraceCTorDTor
+ XFPeek* xfPtr = (XFPeek*)this->xmpFilesRef;
+ printf ( "Default construct TXMPFiles @ %.8X, ref = %.8X, count = %d\n", this, xfPtr, xfPtr->clientRefs );
+ #endif
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_CTorDTorIntro(TXMPFiles)::
+TXMPFiles ( const TXMPFiles<tStringObj> & original ) : xmpFilesRef(original.xmpFilesRef)
+{
+ WXMPFiles_IncrementRefCount_1 ( this->xmpFilesRef );
+ #if XMPFiles_TraceCTorDTor
+ XFPeek* xfPtr = (XFPeek*)this->xmpFilesRef;
+ printf ( "Copy construct TXMPFiles @ %.8X, ref = %.8X, count = %d\n", this, xfPtr, xfPtr->clientRefs );
+ #endif
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,void)::
+operator= ( const TXMPFiles<tStringObj> & rhs )
+{
+ #if XMPFiles_TraceCTorDTor
+ XFPeek* xfLHS = (XFPeek*)this->xmpFilesRef;
+ XFPeek* xfRHS = (XFPeek*)rhs.xmpFilesRef;
+ printf ( "Assign TXMPFiles, lhs @ %.8X, rhs @ %.8X\n", this, &rhs );
+ printf ( " original lhs ref = %.8X, count = %d\n", xfLHS, xfLHS->clientRefs );
+ printf ( " original rhs ref = %.8X, count = %d\n", xfRHS, xfRHS->clientRefs );
+ #endif
+ XMPFilesRef oldRef = this->xmpFilesRef; // ! Decrement last so errors leave client object OK.
+ this->xmpFilesRef = rhs.xmpFilesRef;
+ WXMPFiles_IncrementRefCount_1 ( this->xmpFilesRef ); // Increment the count on the new ref.
+ WXMPFiles_DecrementRefCount_1 ( oldRef ); // Decrement the count on the old ref.
+ #if XMPFiles_TraceCTorDTor
+ printf ( " result lhs ref = %.8X, count = %d\n", xfLHS, xfLHS->clientRefs );
+ #endif
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_CTorDTorIntro(TXMPFiles)::
+TXMPFiles ( XMPFilesRef _xmpFilesRef ) : xmpFilesRef(_xmpFilesRef)
+{
+ WXMPFiles_IncrementRefCount_1 ( this->xmpFilesRef );
+ #if XMPFiles_TraceCTorDTor
+ XFPeek* xfPtr = (XFPeek*)this->xmpFilesRef;
+ printf ( "Ref construct TXMPFiles @ %.8X, ref = %.8X, count = %d\n", this, xfPtr, xfPtr->clientRefs );
+ #endif
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_CTorDTorIntro(TXMPFiles)::
+TXMPFiles ( XMP_StringPtr filePath,
+ XMP_FileFormat format /* = kXMP_UnknownFile */,
+ XMP_OptionBits openFlags /* = 0 */ ) : xmpFilesRef(Default_CTor())
+{
+ #if XMPFiles_TraceCTorDTor
+ XFPeek* xfPtr = (XFPeek*)this->xmpFilesRef;
+ printf ( "File construct TXMPFiles @ %.8X, ref = %.8X, count = %d\n", this, xfPtr, xfPtr->clientRefs );
+ #endif
+ bool ok = this->OpenFile ( filePath, format, openFlags );
+ if ( ! ok ) throw XMP_Error ( kXMPErr_NoFileHandler, "OpenFile returned false" );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_CTorDTorIntro(TXMPFiles)::
+TXMPFiles ( const tStringObj & filePath,
+ XMP_FileFormat format /* = kXMP_UnknownFile */,
+ XMP_OptionBits openFlags /* = 0 */ ) : xmpFilesRef(Default_CTor())
+{
+ #if XMPFiles_TraceCTorDTor
+ XFPeek* xfPtr = (XFPeek*)this->xmpFilesRef;
+ printf ( "File construct TXMPFiles @ %.8X, ref = %.8X, count = %d\n", this, xfPtr, xfPtr->clientRefs );
+ #endif
+ bool ok = this->OpenFile ( filePath.c_str(), format, openFlags );
+ if ( ! ok ) throw XMP_Error ( kXMPErr_NoFileHandler, "OpenFile returned false" );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_CTorDTorIntro(TXMPFiles)::
+~TXMPFiles () throw()
+{
+ #if XMPFiles_TraceCTorDTor
+ XFPeek* xfPtr = (XFPeek*)this->xmpFilesRef;
+ printf ( "Destruct TXMPFiles @ %.8X, ref= %.8X, count = %d\n", this, xfPtr, xfPtr->clientRefs );
+ #endif
+ WXMPFiles_DecrementRefCount_1 ( this->xmpFilesRef );
+ this->xmpFilesRef = 0;
+}
+
+// =================================================================================================
+
+XMP_MethodIntro(TXMPFiles,bool)::
+GetFormatInfo ( XMP_FileFormat format,
+ XMP_OptionBits * flags )
+{
+ WrapCheckBool ( found, zXMPFiles_GetFormatInfo_1 ( format, flags ) );
+ return found;
+}
+
+// =================================================================================================
+
+XMP_MethodIntro(TXMPFiles,XMPFilesRef)::
+GetInternalRef()
+{
+ return this->xmpFilesRef;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,XMP_FileFormat)::
+CheckFileFormat ( XMP_StringPtr filePath )
+{
+ WrapCheckFormat ( format, zXMPFiles_CheckFileFormat_1 ( filePath ) );
+ return format;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,XMP_FileFormat)::
+CheckPackageFormat ( XMP_StringPtr folderPath )
+{
+ WrapCheckFormat ( format, zXMPFiles_CheckPackageFormat_1 ( folderPath ) );
+ return format;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,bool)::
+GetFileModDate ( XMP_StringPtr filePath, XMP_DateTime * modDate, XMP_FileFormat * format, XMP_OptionBits options )
+{
+ WrapCheckBool ( ok, zXMPFiles_GetFileModDate_1 ( filePath, modDate, format, options ) );
+ return ok;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,bool)::
+GetAssociatedResources ( XMP_StringPtr filePath,
+ std::vector<tStringObj>* resourceList,
+ XMP_FileFormat format /* = kXMP_UnknownFile */,
+ XMP_OptionBits options /* = 0 */)
+{
+ WrapCheckBool ( ok, zXMPFiles_GetAssociatedResources_1 ( filePath, resourceList, format, options, SetClientStringVector ) );
+ return ok;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,bool)::
+IsMetadataWritable ( XMP_StringPtr filePath,
+ bool * writable,
+ XMP_FileFormat format /* = kXMP_UnknownFile */,
+ XMP_OptionBits options /* = 0 */)
+{
+ if ( writable)
+ {
+ XMP_Bool internalWritable = ConvertBoolToXMP_Bool( *writable );
+ WrapCheckBool ( ok, zXMPFiles_IsMetadataWritable_1 ( filePath, &internalWritable, format, options ) );
+ *writable = ConvertXMP_BoolToBool( internalWritable );
+ return ok;
+ }
+ else
+ {
+ WrapCheckBool ( ok, zXMPFiles_IsMetadataWritable_1 ( filePath, NULL, format, options ) );
+ return ok;
+ }
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,bool)::
+OpenFile ( XMP_StringPtr filePath,
+ XMP_FileFormat format /* = kXMP_UnknownFile */,
+ XMP_OptionBits openFlags /* = 0 */ )
+{
+ WrapCheckBool ( ok, zXMPFiles_OpenFile_1 ( filePath, format, openFlags ) );
+ return ok;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,bool)::
+OpenFile ( const tStringObj & filePath,
+ XMP_FileFormat format /* = kXMP_UnknownFile */,
+ XMP_OptionBits openFlags /* = 0 */ )
+{
+ return this->OpenFile ( filePath.c_str(), format, openFlags );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+#if XMP_StaticBuild // ! Client XMP_IO objects can only be used in static builds.
+XMP_MethodIntro(TXMPFiles,bool)::
+OpenFile ( XMP_IO * clientIO,
+ XMP_FileFormat format /* = kXMP_UnknownFile */,
+ XMP_OptionBits openFlags /* = 0 */ )
+{
+ WrapCheckBool ( ok, zXMPFiles_OpenFile_2 ( clientIO, format, openFlags ) );
+ return ok;
+}
+#endif
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,void)::
+CloseFile ( XMP_OptionBits closeFlags /* = 0 */ )
+{
+ WrapCheckVoid ( zXMPFiles_CloseFile_1 ( closeFlags ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,bool)::
+GetFileInfo ( tStringObj * filePath /* = 0 */,
+ XMP_OptionBits * openFlags /* = 0 */,
+ XMP_FileFormat * format /* = 0 */,
+ XMP_OptionBits * handlerFlags /* = 0 */ )
+{
+ WrapCheckBool ( isOpen, zXMPFiles_GetFileInfo_1 ( filePath, openFlags, format, handlerFlags, SetClientString ) );
+ return isOpen;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,void)::
+SetAbortProc ( XMP_AbortProc abortProc,
+ void * abortArg )
+{
+ WrapCheckVoid ( zXMPFiles_SetAbortProc_1 ( abortProc, abortArg ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,bool)::
+GetXMP ( SXMPMeta * xmpObj /* = 0 */,
+ tStringObj * xmpPacket /* = 0 */,
+ XMP_PacketInfo * packetInfo /* = 0 */ )
+{
+ XMPMetaRef xmpRef = 0;
+ if ( xmpObj != 0 ) {
+ SXMPUtils::RemoveProperties ( xmpObj, 0, 0, kXMPUtil_DoAllProperties ); // *** Need an SXMPMeta::Clear method:
+ xmpRef = xmpObj->GetInternalRef();
+ }
+
+ WrapCheckBool ( hasXMP, zXMPFiles_GetXMP_1 ( xmpRef, xmpPacket, packetInfo, SetClientString ) );
+ return hasXMP;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,void)::
+PutXMP ( const SXMPMeta & xmpObj )
+{
+ WrapCheckVoid ( zXMPFiles_PutXMP_1 ( xmpObj.GetInternalRef(), 0, 0 ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,void)::
+PutXMP ( XMP_StringPtr xmpPacket,
+ XMP_StringLen xmpLength /* = kXMP_UseNullTermination */ )
+{
+ WrapCheckVoid ( zXMPFiles_PutXMP_1 ( 0, xmpPacket, xmpLength ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,void)::
+PutXMP ( const tStringObj & xmpPacket )
+{
+ this->PutXMP ( xmpPacket.c_str(), (XMP_StringLen)xmpPacket.size() );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,bool)::
+CanPutXMP ( const SXMPMeta & xmpObj )
+{
+ WrapCheckBool ( canPut, zXMPFiles_CanPutXMP_1 ( xmpObj.GetInternalRef(), 0, 0 ) );
+ return canPut;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,bool)::
+CanPutXMP ( XMP_StringPtr xmpPacket,
+ XMP_StringLen xmpLength /* = kXMP_UseNullTermination */ )
+{
+ WrapCheckBool ( canPut, zXMPFiles_CanPutXMP_1 ( 0, xmpPacket, xmpLength ) );
+ return canPut;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,bool)::
+CanPutXMP ( const tStringObj & xmpPacket )
+{
+ return this->CanPutXMP ( xmpPacket.c_str(), (XMP_StringLen)xmpPacket.size() );
+}
+
+// =================================================================================================
+
+XMP_MethodIntro(TXMPFiles,void)::
+SetDefaultProgressCallback ( XMP_ProgressReportProc proc, void * context /* = 0 */,
+ float interval /* = 1.0 */, bool sendStartStop /* = false */ )
+{
+ XMP_Bool internalsendStartStop = ConvertBoolToXMP_Bool( sendStartStop );
+ WrapCheckVoid ( zXMPFiles_SetDefaultProgressCallback_1 ( proc, context, interval, internalsendStartStop ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,void)::
+SetProgressCallback ( XMP_ProgressReportProc proc, void * context /* = 0 */,
+ float interval /* = 1.0 */, bool sendStartStop /* = false */ )
+{
+ XMP_Bool internalsendStartStop = ConvertBoolToXMP_Bool( sendStartStop );
+ WrapCheckVoid ( zXMPFiles_SetProgressCallback_1 ( proc, context, interval, internalsendStartStop ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,void)::
+SetDefaultErrorCallback ( XMPFiles_ErrorCallbackProc proc,
+ void * context /* = 0 */, XMP_Uns32 limit /*= 1 */ )
+{
+ WrapCheckVoid ( zXMPFiles_SetDefaultErrorCallback_1 ( proc, context, limit ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,void)::
+SetErrorCallback ( XMPFiles_ErrorCallbackProc proc,
+ void * context /* = 0 */, XMP_Uns32 limit /*= 1 */ )
+{
+ WrapCheckVoid ( zXMPFiles_SetErrorCallback_1 ( proc, context, limit ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPFiles,void)::
+ResetErrorCallbackLimit ( XMP_Uns32 limit /* = 1 */ )
+{
+ WrapCheckVoid ( zXMPFiles_ResetErrorCallbackLimit_1 ( limit ) );
+}
+
+// =================================================================================================
diff --git a/gpr/source/lib/xmp_core/public/include/client-glue/TXMPIterator.incl_cpp b/gpr/source/lib/xmp_core/public/include/client-glue/TXMPIterator.incl_cpp
new file mode 100644
index 0000000..0b39d01
--- /dev/null
+++ b/gpr/source/lib/xmp_core/public/include/client-glue/TXMPIterator.incl_cpp
@@ -0,0 +1,223 @@
+// =================================================================================================
+// ADOBE SYSTEMS INCORPORATED
+// Copyright 2002 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.
+// =================================================================================================
+
+// ================================================================================================
+/// \file TXMPIterator.incl_cpp
+/// \brief The implementation of the TXMPIterator template class.
+
+#include "XMP.hpp"
+#include "client-glue/WXMP_Common.hpp"
+#include "client-glue/WXMPIterator.hpp"
+
+// =================================================================================================
+// Implementation Guidelines
+// =========================
+//
+// The implementations of the template functions are very stylized. The jobs done in this code are:
+//
+// 1. Set up the xmpIter template data member in the constructors.
+// 2. Call through to the appropriate WXMPIterator function.
+// 3. Copy returned strings and release the threading lock.
+//
+// The various kinds of functions follow similar patterns, first assuming no returned string:
+//
+// Constructors - Use an initializer for the xmpIter data member to call the WXMPIterator constructor.
+// Destructor - Let the WXMPIterator destructor be implicitly called for the xmpIter data member.
+// Static function - Simply call the corresponding WXMPIterator static function.
+// Non-static function - Simply call the corresponding WXMPIterator function using xmpIter.
+//
+// If a member function has returned strings the code looks roughly like this:
+//
+// <<<callthrough>>>
+// <<<checkfailure>>>
+// if ( <<<appropriate>>> ) {
+// if ( outStr != 0 ) outStr->assign ( outPtr, outLen );
+// <<<unlock>>>
+// }
+// return result;
+//
+// The <<<callthrough>>> is the call to the wrapper, and <<<checkfailure>>> is the check and throw
+// if the wrapper reports failure. The <<<appropriate>>> check is used to determine if the string
+// should actually be assigned. For example, GetProperty can't assign the value if the property
+// does not exist. There is no <<<appropriate>>> check if it isn't, well, appropriate. Outputs are
+// always passed as explicit pointers, and null can be passed if the string is not wanted. The
+// inner implementation holds the threading lock if an output string is returned, regardless of
+// whether the client wants it or not (which the implementation does not know).
+//
+// =================================================================================================
+
+#ifndef XMP_TraceCTorDTor
+ #define XMP_TraceCTorDTor 0
+#endif
+
+#if XMP_TraceCTorDTor
+ class XIPeek { // Hack to peek at the client ref count in the internal object.
+ public:
+ XIPeek();
+ virtual ~XIPeek();
+ XMP_Int32 clientRefs;
+ };
+#endif
+
+// -------------------------------------------------------------------------------------------------
+
+#define PropIterCTor(xmpRef,schemaNS,propName,options) \
+ WrapCheckIterRef ( newRef, zXMPIterator_PropCTor_1 ( xmpRef, schemaNS, propName, options ) ); \
+ this->iterRef = newRef
+
+// -------------------------------------------------------------------------------------------------
+
+#define TableIterCTor(schemaNS,propName,options) \
+ WrapCheckIterRef ( newRef, zXMPIterator_TableCTor_1 ( schemaNS, propName, options ) ); \
+ this->iterRef = newRef
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPIterator,void)::
+SetClientString ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen )
+{
+ tStringObj * clientStr = (tStringObj*) clientPtr;
+ clientStr->assign ( valuePtr, valueLen );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_CTorDTorIntro(TXMPIterator)::
+TXMPIterator ( const TXMPIterator<tStringObj> & original ) : iterRef(original.iterRef)
+{
+ WXMPIterator_IncrementRefCount_1 ( this->iterRef );
+ #if XMP_TraceCTorDTor
+ XIPeek* xiPtr = (XIPeek*)this->iterRef;
+ printf ( "Copy construct TXMPIterator @ %.8X, ref = %.8X, count = %d\n", this, xiPtr, xiPtr->clientRefs );
+ #endif
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPIterator,void)::
+operator= ( const TXMPIterator<tStringObj> & rhs )
+{
+ #if XMP_TraceCTorDTor
+ XIPeek* xiLHS = (XIPeek*)this->iterRef;
+ XIPeek* xiRHS = (XIPeek*)rhs.iterRef;
+ printf ( "Assign TXMPIterator, lhs @ %.8X, rhs @ %.8X\n", this, &rhs );
+ printf ( " original lhs ref = %.8X, count = %d\n", xiLHS, xiLHS->clientRefs );
+ printf ( " original rhs ref = %.8X, count = %d\n", xiRHS, xiRHS->clientRefs );
+ #endif
+ XMPIteratorRef oldRef = this->iterRef; // ! Decrement last so errors leave client object OK.
+ this->iterRef = rhs.iterRef;
+ WXMPIterator_IncrementRefCount_1 ( this->iterRef );
+ WXMPIterator_DecrementRefCount_1 ( oldRef );
+ #if XMP_TraceCTorDTor
+ printf ( " result lhs ref = %.8X, count = %d\n", xiLHS, xiLHS->clientRefs );
+ #endif
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_CTorDTorIntro(TXMPIterator)::
+TXMPIterator() : iterRef(0)
+{
+ throw XMP_Error ( kXMPErr_Unavailable, "No default construction for XMP iterators" );
+ #if XMP_TraceCTorDTor
+ XIPeek* xiPtr = (XIPeek*)this->iterRef;
+ printf ( "Default construct TXMPIterator @ %.8X, ref = %.8X, count = %d\n", this, xiPtr, xiPtr->clientRefs );
+ #endif
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_CTorDTorIntro(TXMPIterator)::
+TXMPIterator ( const TXMPMeta<tStringObj> & xmpObj,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_OptionBits options /* = 0 */ ) : iterRef(0)
+{
+ PropIterCTor ( xmpObj.GetInternalRef(), schemaNS, propName, options );
+ #if XMP_TraceCTorDTor
+ XIPeek* xiPtr = (XIPeek*)this->iterRef;
+ printf ( "Construct property TXMPIterator @ %.8X, ref = %.8X, count = %d\n", this, xiPtr, xiPtr->clientRefs );
+ #endif
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_CTorDTorIntro(TXMPIterator)::
+TXMPIterator ( const TXMPMeta<tStringObj> & xmpObj,
+ XMP_StringPtr schemaNS,
+ XMP_OptionBits options /* = 0 */ ) : iterRef(0)
+{
+ PropIterCTor ( xmpObj.GetInternalRef(), schemaNS, "", options );
+ #if XMP_TraceCTorDTor
+ XIPeek* xiPtr = (XIPeek*)this->iterRef;
+ printf ( "Construct schema TXMPIterator @ %.8X, ref = %.8X, count = %d\n", this, xiPtr, xiPtr->clientRefs );
+ #endif
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_CTorDTorIntro(TXMPIterator)::
+TXMPIterator ( const TXMPMeta<tStringObj> & xmpObj,
+ XMP_OptionBits options /* = 0 */ ) : iterRef(0)
+{
+ PropIterCTor ( xmpObj.GetInternalRef(), "", "", options );
+ #if XMP_TraceCTorDTor
+ XIPeek* xiPtr = (XIPeek*)this->iterRef;
+ printf ( "Construct tree TXMPIterator @ %.8X, ref = %.8X, count = %d\n", this, xiPtr, xiPtr->clientRefs );
+ #endif
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_CTorDTorIntro(TXMPIterator)::
+TXMPIterator ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_OptionBits options ) : iterRef(0)
+{
+ TableIterCTor ( schemaNS, propName, options );
+ #if XMP_TraceCTorDTor
+ XIPeek* xiPtr = (XIPeek*)this->iterRef;
+ printf ( "Construct table TXMPIterator @ %.8X, ref = %.8X, count = %d\n", this, xiPtr, xiPtr->clientRefs );
+ #endif
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_CTorDTorIntro(TXMPIterator)::
+~TXMPIterator () throw()
+{
+ #if XMP_TraceCTorDTor
+ XIPeek* xiPtr = (XIPeek*)this->iterRef;
+ printf ( "Destruct TXMPIterator @ %.8X, ref = %.8X, count = %d\n", this, xiPtr, xiPtr->clientRefs );
+ #endif
+ WXMPIterator_DecrementRefCount_1 ( this->iterRef );
+ this->iterRef = 0;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPIterator,bool)::
+Next ( tStringObj * schemaNS /* = 0 */,
+ tStringObj * propPath /* = 0 */,
+ tStringObj * propValue /* = 0 */,
+ XMP_OptionBits * options /* = 0 */ )
+{
+ WrapCheckBool ( found, zXMPIterator_Next_1 ( schemaNS, propPath, propValue, options, SetClientString ) );
+ return found;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPIterator,void)::
+Skip ( XMP_OptionBits options )
+{
+ WrapCheckVoid ( zXMPIterator_Skip_1 ( options ) );
+}
+
+// =================================================================================================
diff --git a/gpr/source/lib/xmp_core/public/include/client-glue/TXMPMeta.incl_cpp b/gpr/source/lib/xmp_core/public/include/client-glue/TXMPMeta.incl_cpp
new file mode 100644
index 0000000..aa5f4b8
--- /dev/null
+++ b/gpr/source/lib/xmp_core/public/include/client-glue/TXMPMeta.incl_cpp
@@ -0,0 +1,914 @@
+// =================================================================================================
+// ADOBE SYSTEMS INCORPORATED
+// Copyright 2002 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.
+// =================================================================================================
+
+// ================================================================================================
+/// \file TXMPMeta.incl_cpp
+/// \brief The implementation of the TXMPMeta template class.
+
+#include "XMP.hpp"
+
+#include "client-glue/WXMP_Common.hpp"
+
+#include "client-glue/WXMPMeta.hpp"
+
+#if INCLUDE_XMP_NEW_DOM_MODEL
+ #include "XMPCore/XMPCore_Defines.h"
+
+ #if ENABLE_NEW_DOM_MODEL
+ #include "XMPCore/XMPCore_Defines.h"
+ #include "XMPCore/Interfaces/IXMPDOMFactory.h"
+ #endif
+#endif
+
+// =================================================================================================
+// Implementation Guidelines
+// =========================
+//
+// The implementations of the template functions are very stylized. ...
+//
+// =================================================================================================
+
+#ifndef XMP_TraceCTorDTor
+ #define XMP_TraceCTorDTor 0
+#endif
+
+#if XMP_TraceCTorDTor
+ class XMPeek { // Hack to peek at the client ref count in the internal object.
+ public:
+ XMPeek();
+ virtual ~XMPeek();
+ XMP_Int32 clientRefs;
+ };
+#endif
+
+// =================================================================================================
+// Local utilities
+// ===============
+
+class TOPW_Info {
+public:
+ XMP_TextOutputProc clientProc;
+ void * clientRefCon;
+ TOPW_Info ( XMP_TextOutputProc proc, void * refCon ) : clientProc(proc), clientRefCon(refCon) {};
+private:
+ TOPW_Info() {}; // ! Hide default constructor.
+};
+
+static XMP_Status TextOutputProcWrapper ( void * refCon,
+ XMP_StringPtr buffer,
+ XMP_StringLen bufferSize )
+{
+ try { // Don't let client callback exceptions propagate across DLL boundaries.
+ TOPW_Info * info = (TOPW_Info*)refCon;
+ return info->clientProc ( info->clientRefCon, buffer, bufferSize );
+ } catch ( ... ) {
+ return -1;
+ }
+}
+
+// =================================================================================================
+// Initialization and termination
+// ==============================
+
+XMP_MethodIntro(TXMPMeta,void)::
+SetClientString ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen )
+{
+ tStringObj * clientStr = (tStringObj*) clientPtr;
+ clientStr->assign ( valuePtr, valueLen );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+GetVersionInfo ( XMP_VersionInfo * info )
+{
+ WrapNoCheckVoid ( zXMPMeta_GetVersionInfo_1 ( info ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+#if XMP_TraceClientCalls
+ FILE * xmpClientLog = stderr;
+#endif
+
+#ifndef XMP_TypeCheck
+ #if ! XMP_DebugBuild
+ #define XMP_TypeCheck(e,msg) /* nothing */
+ #else
+ #define XMP_TypeCheck(e,msg) if ( ! (e) ) throw XMP_Error ( kXMPErr_AssertFailure, msg );
+ #endif
+#endif
+
+XMP_MethodIntro(TXMPMeta,bool)::
+Initialize()
+{
+ // Verify critical type sizes.
+ XMP_TypeCheck ( (sizeof(XMP_Int8) == 1), "Size wrong for critical type XMP_Int8" );
+ XMP_TypeCheck ( (sizeof(XMP_Int16) == 2), "Size wrong for critical type XMP_Int16" );
+ XMP_TypeCheck ( (sizeof(XMP_Int32) == 4), "Size wrong for critical type XMP_Int32" );
+ XMP_TypeCheck ( (sizeof(XMP_Int64) == 8), "Size wrong for critical type XMP_Int64" );
+ XMP_TypeCheck ( (sizeof(XMP_Uns8) == 1), "Size wrong for critical type XMP_Uns8" );
+ XMP_TypeCheck ( (sizeof(XMP_Uns16) == 2), "Size wrong for critical type XMP_Uns16" );
+ XMP_TypeCheck ( (sizeof(XMP_Uns32) == 4), "Size wrong for critical type XMP_Uns32" );
+ XMP_TypeCheck ( (sizeof(XMP_Uns64) == 8), "Size wrong for critical type XMP_Uns64" );
+ XMP_TypeCheck ( (sizeof(XMP_Bool) == 1), "Size wrong for critical type XMP_Bool" );
+
+ #if XMP_TraceClientCallsToFile
+ xmpClientLog = fopen ( "XMPClientLog.txt", "w" );
+ if ( xmpClientLog == 0 ) xmpClientLog = stderr;
+ #endif
+
+ WrapCheckBool ( ok, zXMPMeta_Initialize_1() );
+
+ #if ENABLE_NEW_DOM_MODEL
+ NS_XMPCOMMON::IXMPDOMFactory_latest::CreateInstance();
+ #endif
+
+ return ok;
+
+}
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+Terminate()
+{
+ WrapNoCheckVoid ( zXMPMeta_Terminate_1() );
+
+ #if XMP_TraceClientCallsToFile
+ if ( xmpClientLog != stderr ) fclose ( xmpClientLog );
+ xmpClientLog = stderr;
+ #endif
+}
+
+// =================================================================================================
+// Constuctors, destructor, operators
+// ==================================
+
+static XMPMetaRef DefaultCTor()
+{
+ WrapCheckMetaRef ( newRef, zXMPMeta_CTor_1() );
+ return newRef;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_CTorDTorIntro(TXMPMeta)::
+TXMPMeta() : xmpRef(DefaultCTor())
+{
+ #if XMP_TraceCTorDTor
+ XMPeek* xmPtr = (XMPeek*)this->xmpRef;
+ printf ( "Default construct TXMPMeta @ %.8X, ref = %.8X, count = %d\n", this, xmPtr, xmPtr->clientRefs );
+ #endif
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_CTorDTorIntro(TXMPMeta)::
+TXMPMeta ( const TXMPMeta<tStringObj> & original ) : xmpRef(original.xmpRef)
+{
+ WXMPMeta_IncrementRefCount_1 ( this->xmpRef );
+ #if XMP_TraceCTorDTor
+ XMPeek* xmPtr = (XMPeek*)this->xmpRef;
+ printf ( "Copy construct TXMPMeta @ %.8X, ref = %.8X, count = %d\n", this, xmPtr, xmPtr->clientRefs );
+ #endif
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+operator= ( const TXMPMeta<tStringObj> & rhs )
+{
+ #if XMP_TraceCTorDTor
+ XMPeek* xmLHS = (XMPeek*)this->xmpRef;
+ XMPeek* xmRHS = (XMPeek*)rhs.xmpRef;
+ printf ( "Assign TXMPMeta, lhs @ %.8X, rhs @ %.8X\n", this, &rhs );
+ printf ( " original lhs ref = %.8X, count = %d\n", xmLHS, xmLHS->clientRefs );
+ printf ( " original rhs ref = %.8X, count = %d\n", xmRHS, xmRHS->clientRefs );
+ #endif
+ XMPMetaRef oldRef = this->xmpRef; // ! Decrement last so errors leave client object OK.
+ this->xmpRef = rhs.xmpRef;
+ WXMPMeta_IncrementRefCount_1 ( this->xmpRef ); // Increment the count on the new ref.
+ WXMPMeta_DecrementRefCount_1 ( oldRef ); // Decrement the count on the old ref.
+ #if XMP_TraceCTorDTor
+ printf ( " result lhs ref = %.8X, count = %d\n", xmLHS, xmLHS->clientRefs );
+ #endif
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_CTorDTorIntro(TXMPMeta)::
+TXMPMeta ( XMPMetaRef _xmpRef ) : xmpRef(_xmpRef)
+{
+ WXMPMeta_IncrementRefCount_1 ( this->xmpRef );
+ #if XMP_TraceCTorDTor
+ XMPeek* xmPtr = (XMPeek*)this->xmpRef;
+ printf ( "Ref construct TXMPMeta @ %.8X, ref = %.8X, count = %d\n", this, xmPtr, xmPtr->clientRefs );
+ #endif
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_CTorDTorIntro(TXMPMeta)::
+TXMPMeta ( XMP_StringPtr buffer,
+ XMP_StringLen xmpSize ) : xmpRef(DefaultCTor())
+{
+ #if XMP_TraceCTorDTor
+ XMPeek* xmPtr = (XMPeek*)this->xmpRef;
+ printf ( "Parse construct TXMPMeta @ %.8X, ref = %.8X, count = %d\n", this, xmPtr, xmPtr->clientRefs );
+ #endif
+ try {
+ this->ParseFromBuffer ( buffer, xmpSize );
+ } catch ( ... ) {
+ WXMPMeta_DecrementRefCount_1 ( this->xmpRef );
+ this->xmpRef = 0;
+ throw;
+ }
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_CTorDTorIntro(TXMPMeta)::
+~TXMPMeta() throw()
+{
+ #if XMP_TraceCTorDTor
+ XMPeek* xmPtr = (XMPeek*)this->xmpRef;
+ printf ( "Destruct TXMPMeta @ %.8X, ref = %.8X, count = %d\n", this, xmPtr, xmPtr->clientRefs );
+ #endif
+ WXMPMeta_DecrementRefCount_1 ( this->xmpRef );
+ this->xmpRef = 0;
+
+} // ~TXMPMeta ()
+
+// =================================================================================================
+// Global state functions
+// ======================
+
+XMP_MethodIntro(TXMPMeta,XMP_OptionBits)::
+GetGlobalOptions()
+{
+ WrapCheckOptions ( options, zXMPMeta_GetGlobalOptions_1() );
+ return options;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+SetGlobalOptions ( XMP_OptionBits options )
+{
+ WrapCheckVoid ( zXMPMeta_SetGlobalOptions_1 ( options ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,XMP_Status)::
+DumpNamespaces ( XMP_TextOutputProc outProc,
+ void * refCon )
+{
+ TOPW_Info info ( outProc, refCon );
+ WrapCheckStatus ( status, zXMPMeta_DumpNamespaces_1 ( TextOutputProcWrapper, &info ) );
+ return status;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,bool)::
+RegisterNamespace ( XMP_StringPtr namespaceURI,
+ XMP_StringPtr suggestedPrefix,
+ tStringObj * registeredPrefix )
+{
+ WrapCheckBool ( prefixMatch, zXMPMeta_RegisterNamespace_1 ( namespaceURI, suggestedPrefix, registeredPrefix, SetClientString ) );
+ return prefixMatch;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,bool)::
+GetNamespacePrefix ( XMP_StringPtr namespaceURI,
+ tStringObj * namespacePrefix )
+{
+ WrapCheckBool ( found, zXMPMeta_GetNamespacePrefix_1 ( namespaceURI, namespacePrefix, SetClientString ) );
+ return found;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,bool)::
+GetNamespaceURI ( XMP_StringPtr namespacePrefix,
+ tStringObj * namespaceURI )
+{
+ WrapCheckBool ( found, zXMPMeta_GetNamespaceURI_1 ( namespacePrefix, namespaceURI, SetClientString ) );
+ return found;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+DeleteNamespace ( XMP_StringPtr namespaceURI )
+{
+ WrapCheckVoid ( zXMPMeta_DeleteNamespace_1 ( namespaceURI ) );
+}
+
+// =================================================================================================
+// Basic property manipulation functions
+// =====================================
+
+XMP_MethodIntro(TXMPMeta,bool)::
+GetProperty ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ tStringObj * propValue,
+ XMP_OptionBits * options ) const
+{
+ WrapCheckBool ( found, zXMPMeta_GetProperty_1 ( schemaNS, propName, propValue, options, SetClientString ) );
+ return found;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,bool)::
+GetArrayItem ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex,
+ tStringObj * itemValue,
+ XMP_OptionBits * options ) const
+{
+ WrapCheckBool ( found, zXMPMeta_GetArrayItem_1 ( schemaNS, arrayName, itemIndex, itemValue, options, SetClientString ) );
+ return found;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,bool)::
+GetStructField ( XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ tStringObj * fieldValue,
+ XMP_OptionBits * options ) const
+{
+ WrapCheckBool ( found, zXMPMeta_GetStructField_1 ( schemaNS, structName, fieldNS, fieldName, fieldValue, options, SetClientString ) );
+ return found;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,bool)::
+GetQualifier ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName,
+ tStringObj * qualValue,
+ XMP_OptionBits * options ) const
+{
+ WrapCheckBool ( found, zXMPMeta_GetQualifier_1 ( schemaNS, propName, qualNS, qualName, qualValue, options, SetClientString ) );
+ return found;
+} //GetQualifier ()
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+SetProperty ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr propValue,
+ XMP_OptionBits options /* = 0 */ )
+{
+ WrapCheckVoid ( zXMPMeta_SetProperty_1 ( schemaNS, propName, propValue, options ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+SetProperty ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ const tStringObj & propValue,
+ XMP_OptionBits options /* = 0 */ )
+{
+ this->SetProperty ( schemaNS, propName, propValue.c_str(), options );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+SetArrayItem ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex,
+ XMP_StringPtr itemValue,
+ XMP_OptionBits options /* = 0 */ )
+{
+ WrapCheckVoid ( zXMPMeta_SetArrayItem_1 ( schemaNS, arrayName, itemIndex, itemValue, options ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+SetArrayItem ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex,
+ const tStringObj & itemValue,
+ XMP_OptionBits options /* = 0 */ )
+{
+ this->SetArrayItem ( schemaNS, arrayName, itemIndex, itemValue.c_str(), options );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+AppendArrayItem ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_OptionBits arrayOptions,
+ XMP_StringPtr itemValue,
+ XMP_OptionBits options /* = 0 */ )
+{
+ WrapCheckVoid ( zXMPMeta_AppendArrayItem_1 ( schemaNS, arrayName, arrayOptions, itemValue, options ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+AppendArrayItem ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_OptionBits arrayOptions,
+ const tStringObj & itemValue,
+ XMP_OptionBits options /* = 0 */ )
+{
+ this->AppendArrayItem ( schemaNS, arrayName, arrayOptions, itemValue.c_str(), options );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+SetStructField ( XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ XMP_StringPtr fieldValue,
+ XMP_OptionBits options /* = 0 */ )
+{
+ WrapCheckVoid ( zXMPMeta_SetStructField_1 ( schemaNS, structName, fieldNS, fieldName, fieldValue, options ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+SetStructField ( XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ const tStringObj & fieldValue,
+ XMP_OptionBits options /* = 0 */ )
+{
+ this->SetStructField ( schemaNS, structName, fieldNS, fieldName, fieldValue.c_str(), options );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+SetQualifier ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName,
+ XMP_StringPtr qualValue,
+ XMP_OptionBits options /* = 0 */ )
+{
+ WrapCheckVoid ( zXMPMeta_SetQualifier_1 ( schemaNS, propName, qualNS, qualName, qualValue, options ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+SetQualifier ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName,
+ const tStringObj & qualValue,
+ XMP_OptionBits options /* = 0 */ )
+{
+ this->SetQualifier ( schemaNS, propName, qualNS, qualName, qualValue.c_str(), options );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+DeleteProperty ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName )
+{
+ WrapCheckVoid ( zXMPMeta_DeleteProperty_1 ( schemaNS, propName ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+DeleteArrayItem ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex )
+{
+ WrapCheckVoid ( zXMPMeta_DeleteArrayItem_1 ( schemaNS, arrayName, itemIndex ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+DeleteStructField ( XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName )
+{
+ WrapCheckVoid ( zXMPMeta_DeleteStructField_1 ( schemaNS, structName, fieldNS, fieldName ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+DeleteQualifier ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName )
+{
+ WrapCheckVoid ( zXMPMeta_DeleteQualifier_1 ( schemaNS, propName, qualNS, qualName ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,bool)::
+DoesPropertyExist ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName ) const
+{
+ WrapCheckBool ( exists, zXMPMeta_DoesPropertyExist_1 ( schemaNS, propName ) );
+ return exists;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,bool)::
+DoesArrayItemExist ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex ) const
+{
+ WrapCheckBool ( exists, zXMPMeta_DoesArrayItemExist_1 ( schemaNS, arrayName, itemIndex ) );
+ return exists;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,bool)::
+DoesStructFieldExist ( XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName ) const
+{
+ WrapCheckBool ( exists, zXMPMeta_DoesStructFieldExist_1 ( schemaNS, structName, fieldNS, fieldName ) );
+ return exists;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,bool)::
+DoesQualifierExist ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName ) const
+{
+ WrapCheckBool ( exists, zXMPMeta_DoesQualifierExist_1 ( schemaNS, propName, qualNS, qualName ) );
+ return exists;
+}
+
+// =================================================================================================
+// Specialized Get and Set functions
+// =================================
+
+XMP_MethodIntro(TXMPMeta,bool)::
+GetLocalizedText ( XMP_StringPtr schemaNS,
+ XMP_StringPtr altTextName,
+ XMP_StringPtr genericLang,
+ XMP_StringPtr specificLang,
+ tStringObj * actualLang,
+ tStringObj * itemValue,
+ XMP_OptionBits * options ) const
+{
+ WrapCheckBool ( found, zXMPMeta_GetLocalizedText_1 ( schemaNS, altTextName, genericLang, specificLang,
+ actualLang, itemValue, options, SetClientString ) );
+ return found;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+SetLocalizedText ( XMP_StringPtr schemaNS,
+ XMP_StringPtr altTextName,
+ XMP_StringPtr genericLang,
+ XMP_StringPtr specificLang,
+ XMP_StringPtr itemValue,
+ XMP_OptionBits options /* = 0 */ )
+{
+ WrapCheckVoid ( zXMPMeta_SetLocalizedText_1 ( schemaNS, altTextName, genericLang, specificLang, itemValue, options ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+SetLocalizedText ( XMP_StringPtr schemaNS,
+ XMP_StringPtr altTextName,
+ XMP_StringPtr genericLang,
+ XMP_StringPtr specificLang,
+ const tStringObj & itemValue,
+ XMP_OptionBits options /* = 0 */ )
+{
+ this->SetLocalizedText ( schemaNS, altTextName, genericLang, specificLang, itemValue.c_str(), options );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+DeleteLocalizedText ( XMP_StringPtr schemaNS,
+ XMP_StringPtr altTextName,
+ XMP_StringPtr genericLang,
+ XMP_StringPtr specificLang )
+{
+ WrapCheckVoid ( zXMPMeta_DeleteLocalizedText_1 ( schemaNS, altTextName, genericLang, specificLang ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,bool)::
+GetProperty_Bool ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ bool * propValue,
+ XMP_OptionBits * options ) const
+{
+ XMP_Bool binValue;
+ WrapCheckBool ( found, zXMPMeta_GetProperty_Bool_1 ( schemaNS, propName, &binValue, options ) );
+ if ( found && (propValue != 0) ) *propValue = binValue;
+ return found;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,bool)::
+GetProperty_Int ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_Int32 * propValue,
+ XMP_OptionBits * options ) const
+{
+ WrapCheckBool ( found, zXMPMeta_GetProperty_Int_1 ( schemaNS, propName, propValue, options ) );
+ return found;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,bool)::
+GetProperty_Int64 ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_Int64 * propValue,
+ XMP_OptionBits * options ) const
+{
+ WrapCheckBool ( found, zXMPMeta_GetProperty_Int64_1 ( schemaNS, propName, propValue, options ) );
+ return found;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,bool)::
+GetProperty_Float ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ double * propValue,
+ XMP_OptionBits * options ) const
+{
+ WrapCheckBool ( found, zXMPMeta_GetProperty_Float_1 ( schemaNS, propName, propValue, options ) );
+ return found;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,bool)::
+GetProperty_Date ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_DateTime * propValue,
+ XMP_OptionBits * options ) const
+{
+ WrapCheckBool ( found, zXMPMeta_GetProperty_Date_1 ( schemaNS, propName, propValue, options ) );
+ return found;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+SetProperty_Bool ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ bool propValue,
+ XMP_OptionBits options /* = 0 */ )
+{
+ WrapCheckVoid ( zXMPMeta_SetProperty_Bool_1 ( schemaNS, propName, propValue, options ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+SetProperty_Int ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_Int32 propValue,
+ XMP_OptionBits options /* = 0 */ )
+{
+ WrapCheckVoid ( zXMPMeta_SetProperty_Int_1 ( schemaNS, propName, propValue, options ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+SetProperty_Int64 ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_Int64 propValue,
+ XMP_OptionBits options /* = 0 */ )
+{
+ WrapCheckVoid ( zXMPMeta_SetProperty_Int64_1 ( schemaNS, propName, propValue, options ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+SetProperty_Float ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ double propValue,
+ XMP_OptionBits options /* = 0 */ )
+{
+ WrapCheckVoid ( zXMPMeta_SetProperty_Float_1 ( schemaNS, propName, propValue, options ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+SetProperty_Date ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ const XMP_DateTime & propValue,
+ XMP_OptionBits options /* = 0 */ )
+{
+ WrapCheckVoid ( zXMPMeta_SetProperty_Date_1 ( schemaNS, propName, propValue, options ) );
+}
+
+// =================================================================================================
+// Miscellaneous Member Functions
+// ==============================
+
+XMP_MethodIntro(TXMPMeta,XMPMetaRef)::
+GetInternalRef() const
+{
+ return this->xmpRef;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+GetObjectName ( tStringObj * nameStr ) const
+{
+ WrapCheckVoid ( zXMPMeta_GetObjectName_1 ( nameStr, SetClientString ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+SetObjectName ( XMP_StringPtr name )
+{
+ WrapCheckVoid ( zXMPMeta_SetObjectName_1 ( name ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+SetObjectName ( tStringObj name )
+{
+ this->SetObjectName ( name.c_str() );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,XMP_OptionBits)::
+GetObjectOptions() const
+{
+ WrapCheckOptions ( options, zXMPMeta_GetObjectOptions_1() );
+ return options;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+SetObjectOptions ( XMP_OptionBits options )
+{
+ WrapCheckVoid ( zXMPMeta_SetObjectOptions_1 ( options ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+Sort()
+{
+ WrapCheckVoid ( zXMPMeta_Sort_1() );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+Erase()
+{
+ WrapCheckVoid ( zXMPMeta_Erase_1() );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,TXMPMeta<tStringObj>)::
+Clone ( XMP_OptionBits options ) const
+{
+ WrapCheckMetaRef ( cloneRef, zXMPMeta_Clone_1 ( options ) );
+ return TXMPMeta<tStringObj> ( cloneRef ); // Ref construct will increment the clientRefs.
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,XMP_Index)::
+CountArrayItems ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName ) const
+{
+ WrapCheckIndex ( count, zXMPMeta_CountArrayItems_1 ( schemaNS, arrayName ) );
+ return count;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,XMP_Status)::
+DumpObject ( XMP_TextOutputProc outProc,
+ void * refCon ) const
+{
+ TOPW_Info info ( outProc, refCon );
+ WrapCheckStatus ( status, zXMPMeta_DumpObject_1 ( TextOutputProcWrapper, &info ) );
+ return status;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+ParseFromBuffer ( XMP_StringPtr buffer,
+ XMP_StringLen bufferSize,
+ XMP_OptionBits options /* = 0 */ )
+{
+ WrapCheckVoid ( zXMPMeta_ParseFromBuffer_1 ( buffer, bufferSize, options ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+SerializeToBuffer ( tStringObj * pktString,
+ XMP_OptionBits options,
+ XMP_StringLen padding,
+ XMP_StringPtr newline,
+ XMP_StringPtr indent,
+ XMP_Index baseIndent /* = 0 */ ) const
+{
+ WrapCheckVoid ( zXMPMeta_SerializeToBuffer_1 ( pktString, options, padding, newline, indent, baseIndent, SetClientString ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+SerializeToBuffer ( tStringObj * pktString,
+ XMP_OptionBits options /* = 0 */,
+ XMP_StringLen padding /* = 0 */ ) const
+{
+ this->SerializeToBuffer ( pktString, options, padding, "", "", 0 );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+SetDefaultErrorCallback ( XMPMeta_ErrorCallbackProc proc,
+ void * context /* = 0 */,
+ XMP_Uns32 limit /* = 1 */ )
+{
+ WrapCheckVoid ( zXMPMeta_SetDefaultErrorCallback_1 ( proc, context, limit ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+SetErrorCallback ( XMPMeta_ErrorCallbackProc proc,
+ void * context /* = 0 */,
+ XMP_Uns32 limit /* = 1 */ )
+{
+ WrapCheckVoid ( zXMPMeta_SetErrorCallback_1 ( proc, context, limit ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPMeta,void)::
+ResetErrorCallbackLimit ( XMP_Uns32 limit /* = 1 */ )
+{
+ WrapCheckVoid ( zXMPMeta_ResetErrorCallbackLimit_1 ( limit ) );
+}
+
+// =================================================================================================
diff --git a/gpr/source/lib/xmp_core/public/include/client-glue/TXMPUtils.incl_cpp b/gpr/source/lib/xmp_core/public/include/client-glue/TXMPUtils.incl_cpp
new file mode 100644
index 0000000..2cd5bae
--- /dev/null
+++ b/gpr/source/lib/xmp_core/public/include/client-glue/TXMPUtils.incl_cpp
@@ -0,0 +1,445 @@
+// =================================================================================================
+// ADOBE SYSTEMS INCORPORATED
+// Copyright 2002 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.
+// =================================================================================================
+
+// ================================================================================================
+/// \file TXMPUtils.incl_cpp
+/// \brief The implementation of the TXMPUtils template class.
+
+#include "XMP.hpp"
+#include "client-glue/WXMP_Common.hpp"
+#include "client-glue/WXMPUtils.hpp"
+
+// =================================================================================================
+// Implementation Guidelines
+// =========================
+//
+// The implementations of the template functions are very stylized. ...
+//
+// =================================================================================================
+
+XMP_MethodIntro(TXMPUtils,void)::
+SetClientString ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen )
+{
+ tStringObj * clientStr = (tStringObj*) clientPtr;
+ clientStr->assign ( valuePtr, valueLen );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+ComposeArrayItemPath ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex,
+ tStringObj * fullPath )
+{
+ WrapCheckVoid ( zXMPUtils_ComposeArrayItemPath_1 ( schemaNS, arrayName, itemIndex, fullPath, SetClientString ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+ComposeStructFieldPath ( XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ tStringObj * fullPath )
+{
+ WrapCheckVoid ( zXMPUtils_ComposeStructFieldPath_1 ( schemaNS, structName, fieldNS, fieldName, fullPath, SetClientString ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+ComposeQualifierPath ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName,
+ tStringObj * fullPath )
+{
+ WrapCheckVoid ( zXMPUtils_ComposeQualifierPath_1 ( schemaNS, propName, qualNS, qualName, fullPath, SetClientString ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+ComposeLangSelector ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr langName,
+ tStringObj * fullPath )
+{
+ WrapCheckVoid ( zXMPUtils_ComposeLangSelector_1 ( schemaNS, arrayName, langName, fullPath, SetClientString ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+ComposeLangSelector ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ const tStringObj & langName,
+ tStringObj * fullPath )
+{
+ TXMPUtils::ComposeLangSelector ( schemaNS, arrayName, langName.c_str(), fullPath );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+ComposeFieldSelector ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ XMP_StringPtr fieldValue,
+ tStringObj * fullPath )
+{
+ WrapCheckVoid ( zXMPUtils_ComposeFieldSelector_1 ( schemaNS, arrayName, fieldNS, fieldName, fieldValue, fullPath, SetClientString ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+ComposeFieldSelector ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ const tStringObj & fieldValue,
+ tStringObj * fullPath )
+{
+ TXMPUtils::ComposeFieldSelector ( schemaNS, arrayName, fieldNS, fieldName, fieldValue.c_str(), fullPath );
+}
+
+// -------------------------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+ConvertFromBool ( bool binValue,
+ tStringObj * strValue )
+{
+ WrapCheckVoid ( zXMPUtils_ConvertFromBool_1 ( binValue, strValue, SetClientString ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+ConvertFromInt ( long binValue,
+ XMP_StringPtr format,
+ tStringObj * strValue )
+{
+ #if XMP_MacBuild & XMP_64 // This is checked because on Mac 64 bit environment, long is of 64 bit and hence gives a warning during implicit
+ // typecasting to XMP_Int32. Now doing it explicitly in that case.
+ WrapCheckVoid ( zXMPUtils_ConvertFromInt_1 ( (XMP_Int32)binValue, format, strValue, SetClientString ) );
+ #else
+ WrapCheckVoid ( zXMPUtils_ConvertFromInt_1 ( binValue, format, strValue, SetClientString ) );
+ #endif
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+ConvertFromInt64 ( long long binValue,
+ XMP_StringPtr format,
+ tStringObj * strValue )
+{
+ WrapCheckVoid ( zXMPUtils_ConvertFromInt64_1 ( binValue, format, strValue, SetClientString ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+ConvertFromFloat ( double binValue,
+ XMP_StringPtr format,
+ tStringObj * strValue )
+{
+ WrapCheckVoid ( zXMPUtils_ConvertFromFloat_1 ( binValue, format, strValue, SetClientString ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+ConvertFromDate ( const XMP_DateTime & binValue,
+ tStringObj * strValue )
+{
+ WrapCheckVoid ( zXMPUtils_ConvertFromDate_1 ( binValue, strValue, SetClientString ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,bool)::
+ConvertToBool ( XMP_StringPtr strValue )
+{
+ WrapCheckBool ( value, zXMPUtils_ConvertToBool_1 ( strValue ) );
+ return value;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,bool)::
+ConvertToBool ( const tStringObj & strValue )
+{
+ return TXMPUtils::ConvertToBool ( strValue.c_str() );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,long)::
+ConvertToInt ( XMP_StringPtr strValue )
+{
+ WrapCheckInt32 ( value, zXMPUtils_ConvertToInt_1 ( strValue ) );
+ return value;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,long)::
+ConvertToInt ( const tStringObj & strValue )
+{
+ return TXMPUtils::ConvertToInt ( strValue.c_str() );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,long long)::
+ConvertToInt64 ( XMP_StringPtr strValue )
+{
+ WrapCheckInt64 ( value, zXMPUtils_ConvertToInt64_1 ( strValue ) );
+ return value;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,long long)::
+ConvertToInt64 ( const tStringObj & strValue )
+{
+ return TXMPUtils::ConvertToInt64 ( strValue.c_str() );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,double)::
+ConvertToFloat ( XMP_StringPtr strValue )
+{
+ WrapCheckFloat ( value, zXMPUtils_ConvertToFloat_1 ( strValue ) );
+ return value;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,double)::
+ConvertToFloat ( const tStringObj & strValue )
+{
+ return TXMPUtils::ConvertToFloat ( strValue.c_str() );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+ConvertToDate ( XMP_StringPtr strValue,
+ XMP_DateTime * binValue )
+{
+ WrapCheckVoid ( zXMPUtils_ConvertToDate_1 ( strValue, binValue ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+ConvertToDate ( const tStringObj & strValue,
+ XMP_DateTime * binValue )
+{
+ TXMPUtils::ConvertToDate ( strValue.c_str(), binValue );
+}
+
+// -------------------------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+CurrentDateTime ( XMP_DateTime * time )
+{
+ WrapCheckVoid ( zXMPUtils_CurrentDateTime_1 ( time ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+SetTimeZone ( XMP_DateTime * time )
+{
+ WrapCheckVoid ( zXMPUtils_SetTimeZone_1 ( time ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+ConvertToUTCTime ( XMP_DateTime * time )
+{
+ WrapCheckVoid ( zXMPUtils_ConvertToUTCTime_1 ( time ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+ConvertToLocalTime ( XMP_DateTime * time )
+{
+ WrapCheckVoid ( zXMPUtils_ConvertToLocalTime_1 ( time ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,int)::
+CompareDateTime ( const XMP_DateTime & left,
+ const XMP_DateTime & right )
+{
+ WrapCheckInt32 ( result, zXMPUtils_CompareDateTime_1 ( left, right ) );
+ return result;
+}
+
+// -------------------------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+EncodeToBase64 ( XMP_StringPtr rawStr,
+ XMP_StringLen rawLen,
+ tStringObj * encodedStr )
+{
+ WrapCheckVoid ( zXMPUtils_EncodeToBase64_1 ( rawStr, rawLen, encodedStr, SetClientString ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+EncodeToBase64 ( const tStringObj & rawStr,
+ tStringObj * encodedStr )
+{
+ TXMPUtils::EncodeToBase64 ( rawStr.c_str(), (XMP_StringLen)rawStr.size(), encodedStr );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+DecodeFromBase64 ( XMP_StringPtr encodedStr,
+ XMP_StringLen encodedLen,
+ tStringObj * rawStr )
+{
+ WrapCheckVoid ( zXMPUtils_DecodeFromBase64_1 ( encodedStr, encodedLen, rawStr, SetClientString ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+DecodeFromBase64 ( const tStringObj & encodedStr,
+ tStringObj * rawStr )
+{
+ TXMPUtils::DecodeFromBase64 ( encodedStr.c_str(), (XMP_StringLen)encodedStr.size(), rawStr );
+}
+
+// -------------------------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------------------------
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+PackageForJPEG ( const TXMPMeta<tStringObj> & xmpObj,
+ tStringObj * standardXMP,
+ tStringObj * extendedXMP,
+ tStringObj * extendedDigest )
+{
+ WrapCheckVoid ( zXMPUtils_PackageForJPEG_1 ( xmpObj.GetInternalRef(), standardXMP, extendedXMP, extendedDigest, SetClientString ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+MergeFromJPEG ( TXMPMeta<tStringObj> * fullXMP,
+ const TXMPMeta<tStringObj> & extendedXMP )
+{
+ WrapCheckVoid ( zXMPUtils_MergeFromJPEG_1 ( fullXMP->GetInternalRef(), extendedXMP.GetInternalRef() ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+CatenateArrayItems ( const TXMPMeta<tStringObj> & xmpObj,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr separator,
+ XMP_StringPtr quotes,
+ XMP_OptionBits options,
+ tStringObj * catedStr )
+{
+ WrapCheckVoid ( zXMPUtils_CatenateArrayItems_1 ( xmpObj.GetInternalRef(), schemaNS, arrayName,
+ separator, quotes, options, catedStr, SetClientString ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+SeparateArrayItems ( TXMPMeta<tStringObj> * xmpObj,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_OptionBits options,
+ XMP_StringPtr catedStr )
+{
+ if ( xmpObj == 0 ) throw XMP_Error ( kXMPErr_BadParam, "Null output SXMPMeta pointer" );
+ WrapCheckVoid ( zXMPUtils_SeparateArrayItems_1 ( xmpObj->GetInternalRef(), schemaNS, arrayName, options, catedStr ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+SeparateArrayItems ( TXMPMeta<tStringObj> * xmpObj,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_OptionBits options,
+ const tStringObj & catedStr )
+{
+ TXMPUtils::SeparateArrayItems ( xmpObj, schemaNS, arrayName, options, catedStr.c_str() );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+ApplyTemplate ( TXMPMeta<tStringObj> * workingXMP,
+ const TXMPMeta<tStringObj> & templateXMP,
+ XMP_OptionBits actions )
+{
+ if ( workingXMP == 0 ) throw XMP_Error ( kXMPErr_BadParam, "Null working SXMPMeta pointer" );
+ WrapCheckVoid ( zXMPUtils_ApplyTemplate_1 ( workingXMP->GetInternalRef(), templateXMP.GetInternalRef(), actions ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+RemoveProperties ( TXMPMeta<tStringObj> * xmpObj,
+ XMP_StringPtr schemaNS /* = 0 */,
+ XMP_StringPtr propName /* = 0 */,
+ XMP_OptionBits options /* = 0 */ )
+{
+ if ( xmpObj == 0 ) throw XMP_Error ( kXMPErr_BadParam, "Null output SXMPMeta pointer" );
+ WrapCheckVoid ( zXMPUtils_RemoveProperties_1 ( xmpObj->GetInternalRef(), schemaNS, propName, options ) );
+}
+
+// -------------------------------------------------------------------------------------------------
+
+// -------------------------------------------------------------------------------------------------
+
+XMP_MethodIntro(TXMPUtils,void)::
+DuplicateSubtree ( const TXMPMeta<tStringObj> & source,
+ TXMPMeta<tStringObj> * dest,
+ XMP_StringPtr sourceNS,
+ XMP_StringPtr sourceRoot,
+ XMP_StringPtr destNS /*= 0 */,
+ XMP_StringPtr destRoot /* = 0 */,
+ XMP_OptionBits options /* = 0 */ )
+{
+ if ( dest == 0 ) throw XMP_Error ( kXMPErr_BadParam, "Null output SXMPMeta pointer" );
+ WrapCheckVoid ( zXMPUtils_DuplicateSubtree_1 ( source.GetInternalRef(), dest->GetInternalRef(),
+ sourceNS, sourceRoot, destNS, destRoot, options ) );
+}
+
+// =================================================================================================
+
+// =================================================================================================
diff --git a/gpr/source/lib/xmp_core/public/include/client-glue/WXMPFiles.hpp b/gpr/source/lib/xmp_core/public/include/client-glue/WXMPFiles.hpp
new file mode 100644
index 0000000..648a842
--- /dev/null
+++ b/gpr/source/lib/xmp_core/public/include/client-glue/WXMPFiles.hpp
@@ -0,0 +1,281 @@
+#ifndef __WXMPFiles_hpp__
+#define __WXMPFiles_hpp__ 1
+
+// =================================================================================================
+// ADOBE SYSTEMS INCORPORATED
+// Copyright 2002 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.
+// =================================================================================================
+
+#include "client-glue/WXMP_Common.hpp"
+
+#if XMP_StaticBuild // ! Client XMP_IO objects can only be used in static builds.
+ #include "XMP_IO.hpp"
+#endif
+
+#if __cplusplus
+extern "C" {
+#endif
+
+// =================================================================================================
+/// \file WXMPFiles.h
+/// \brief High level support to access metadata in files of interest to Adobe applications.
+///
+/// This header ...
+///
+// =================================================================================================
+
+// =================================================================================================
+
+#define WrapCheckXMPFilesRef(result,WCallProto) \
+ WXMP_Result wResult; \
+ WCallProto; \
+ PropagateException ( wResult ); \
+ XMPFilesRef result = XMPFilesRef(wResult.ptrResult)
+
+static XMP_Bool WrapProgressReport ( XMP_ProgressReportProc proc, void * context,
+ float elapsedTime, float fractionDone, float secondsToGo )
+{
+ bool ok;
+ try {
+ ok = (*proc) ( context, elapsedTime, fractionDone, secondsToGo );
+ } catch ( ... ) {
+ ok = false;
+ }
+ return ConvertBoolToXMP_Bool( ok );
+}
+
+// =================================================================================================
+
+static XMP_Bool WrapFilesErrorNotify ( XMPFiles_ErrorCallbackProc proc, void * context,
+ XMP_StringPtr filePath, XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr message )
+{
+ bool ok;
+ try {
+ ok = (*proc) ( context, filePath, severity, cause, message );
+ } catch ( ... ) {
+ ok = false;
+ }
+ return ConvertBoolToXMP_Bool( ok );
+}
+
+// =================================================================================================
+
+#define zXMPFiles_GetVersionInfo_1(versionInfo) \
+ WXMPFiles_GetVersionInfo_1 ( versionInfo /* no wResult */ )
+
+#define zXMPFiles_Initialize_1(options) \
+ WXMPFiles_Initialize_1 ( options, &wResult )
+
+#define zXMPFiles_Initialize_2(options,pluginFolder,plugins) \
+ WXMPFiles_Initialize_2 ( options, pluginFolder, plugins, &wResult )
+
+#define zXMPFiles_Terminate_1() \
+ WXMPFiles_Terminate_1 ( /* no wResult */ )
+
+#define zXMPFiles_CTor_1() \
+ WXMPFiles_CTor_1 ( &wResult )
+
+#define zXMPFiles_GetFormatInfo_1(format,flags) \
+ WXMPFiles_GetFormatInfo_1 ( format, flags, &wResult )
+
+#define zXMPFiles_CheckFileFormat_1(filePath) \
+ WXMPFiles_CheckFileFormat_1 ( filePath, &wResult )
+
+#define zXMPFiles_CheckPackageFormat_1(folderPath) \
+ WXMPFiles_CheckPackageFormat_1 ( folderPath, &wResult )
+
+#define zXMPFiles_GetFileModDate_1(filePath,modDate,format,options) \
+ WXMPFiles_GetFileModDate_1 ( filePath, modDate, format, options, &wResult )
+
+#define zXMPFiles_GetAssociatedResources_1( filePath, resourceList, format, options, SetClientStringVector ) \
+ WXMPFiles_GetAssociatedResources_1 ( filePath, resourceList, format, options, SetClientStringVector, &wResult )
+
+#define zXMPFiles_IsMetadataWritable_1( filePath, writable, format, options ) \
+ WXMPFiles_IsMetadataWritable_1 ( filePath, writable, format, options, &wResult )
+
+#define zXMPFiles_OpenFile_1(filePath,format,openFlags) \
+ WXMPFiles_OpenFile_1 ( this->xmpFilesRef, filePath, format, openFlags, &wResult )
+
+#if XMP_StaticBuild // ! Client XMP_IO objects can only be used in static builds.
+#define zXMPFiles_OpenFile_2(clientIO,format,openFlags) \
+ WXMPFiles_OpenFile_2 ( this->xmpFilesRef, clientIO, format, openFlags, &wResult )
+#endif
+
+#define zXMPFiles_CloseFile_1(closeFlags) \
+ WXMPFiles_CloseFile_1 ( this->xmpFilesRef, closeFlags, &wResult )
+
+#define zXMPFiles_GetFileInfo_1(clientPath,openFlags,format,handlerFlags,SetClientString) \
+ WXMPFiles_GetFileInfo_1 ( this->xmpFilesRef, clientPath, openFlags, format, handlerFlags, SetClientString, &wResult )
+
+#define zXMPFiles_SetAbortProc_1(abortProc,abortArg) \
+ WXMPFiles_SetAbortProc_1 ( this->xmpFilesRef, abortProc, abortArg, &wResult )
+
+#define zXMPFiles_GetXMP_1(xmpRef,clientPacket,packetInfo,SetClientString) \
+ WXMPFiles_GetXMP_1 ( this->xmpFilesRef, xmpRef, clientPacket, packetInfo, SetClientString, &wResult )
+
+#define zXMPFiles_PutXMP_1(xmpRef,xmpPacket,xmpPacketLen) \
+ WXMPFiles_PutXMP_1 ( this->xmpFilesRef, xmpRef, xmpPacket, xmpPacketLen, &wResult )
+
+#define zXMPFiles_CanPutXMP_1(xmpRef,xmpPacket,xmpPacketLen) \
+ WXMPFiles_CanPutXMP_1 ( this->xmpFilesRef, xmpRef, xmpPacket, xmpPacketLen, &wResult )
+
+#define zXMPFiles_SetDefaultProgressCallback_1(proc,context,interval,sendStartStop) \
+ WXMPFiles_SetDefaultProgressCallback_1 ( WrapProgressReport, proc, context, interval, sendStartStop, &wResult )
+
+#define zXMPFiles_SetProgressCallback_1(proc,context,interval,sendStartStop) \
+ WXMPFiles_SetProgressCallback_1 ( this->xmpFilesRef, WrapProgressReport, proc, context, interval, sendStartStop, &wResult )
+
+#define zXMPFiles_SetDefaultErrorCallback_1(proc,context,limit) \
+ WXMPFiles_SetDefaultErrorCallback_1 ( WrapFilesErrorNotify, proc, context, limit, &wResult )
+
+#define zXMPFiles_SetErrorCallback_1(proc,context,limit) \
+ WXMPFiles_SetErrorCallback_1 ( this->xmpFilesRef, WrapFilesErrorNotify, proc, context, limit, &wResult )
+
+#define zXMPFiles_ResetErrorCallbackLimit_1(limit) \
+ WXMPFiles_ResetErrorCallbackLimit_1 ( this->xmpFilesRef, limit, &wResult )
+
+// =================================================================================================
+
+extern void WXMPFiles_GetVersionInfo_1 ( XMP_VersionInfo * versionInfo );
+
+extern void WXMPFiles_Initialize_1 ( XMP_OptionBits options,
+ WXMP_Result * result );
+
+extern void WXMPFiles_Initialize_2 ( XMP_OptionBits options,
+ const char* pluginFolder,
+ const char* plugins,
+ WXMP_Result * result );
+
+extern void WXMPFiles_Terminate_1();
+
+extern void WXMPFiles_CTor_1 ( WXMP_Result * result );
+
+extern void WXMPFiles_IncrementRefCount_1 ( XMPFilesRef xmpFilesRef );
+
+extern void WXMPFiles_DecrementRefCount_1 ( XMPFilesRef xmpFilesRef );
+
+extern void WXMPFiles_GetFormatInfo_1 ( XMP_FileFormat format,
+ XMP_OptionBits * flags, // ! Can be null.
+ WXMP_Result * result );
+
+extern void WXMPFiles_CheckFileFormat_1 ( XMP_StringPtr filePath,
+ WXMP_Result * result );
+
+extern void WXMPFiles_CheckPackageFormat_1 ( XMP_StringPtr folderPath,
+ WXMP_Result * result );
+
+extern void WXMPFiles_GetFileModDate_1 ( XMP_StringPtr filePath,
+ XMP_DateTime * modDate,
+ XMP_FileFormat * format, // ! Can be null.
+ XMP_OptionBits options,
+ WXMP_Result * result );
+
+
+extern void WXMPFiles_GetAssociatedResources_1 ( XMP_StringPtr filePath,
+ void * resourceList,
+ XMP_FileFormat format,
+ XMP_OptionBits options,
+ SetClientStringVectorProc SetClientStringVector,
+ WXMP_Result * result );
+
+extern void WXMPFiles_IsMetadataWritable_1 ( XMP_StringPtr filePath,
+ XMP_Bool * writable,
+ XMP_FileFormat format,
+ XMP_OptionBits options,
+ WXMP_Result * result );
+
+extern void WXMPFiles_OpenFile_1 ( XMPFilesRef xmpFilesRef,
+ XMP_StringPtr filePath,
+ XMP_FileFormat format,
+ XMP_OptionBits openFlags,
+ WXMP_Result * result );
+
+#if XMP_StaticBuild // ! Client XMP_IO objects can only be used in static builds.
+extern void WXMPFiles_OpenFile_2 ( XMPFilesRef xmpFilesRef,
+ XMP_IO * clientIO,
+ XMP_FileFormat format,
+ XMP_OptionBits openFlags,
+ WXMP_Result * result );
+#endif
+
+extern void WXMPFiles_CloseFile_1 ( XMPFilesRef xmpFilesRef,
+ XMP_OptionBits closeFlags,
+ WXMP_Result * result );
+
+extern void WXMPFiles_GetFileInfo_1 ( XMPFilesRef xmpFilesRef,
+ void * clientPath,
+ XMP_OptionBits * openFlags, // ! Can be null.
+ XMP_FileFormat * format, // ! Can be null.
+ XMP_OptionBits * handlerFlags, // ! Can be null.
+ SetClientStringProc SetClientString,
+ WXMP_Result * result );
+
+extern void WXMPFiles_SetAbortProc_1 ( XMPFilesRef xmpFilesRef,
+ XMP_AbortProc abortProc,
+ void * abortArg,
+ WXMP_Result * result );
+
+extern void WXMPFiles_GetXMP_1 ( XMPFilesRef xmpFilesRef,
+ XMPMetaRef xmpRef, // ! Can be null.
+ void * clientPacket,
+ XMP_PacketInfo * packetInfo, // ! Can be null.
+ SetClientStringProc SetClientString,
+ WXMP_Result * result );
+
+extern void WXMPFiles_PutXMP_1 ( XMPFilesRef xmpFilesRef,
+ XMPMetaRef xmpRef, // ! Only one of the XMP object or packet are passed.
+ XMP_StringPtr xmpPacket,
+ XMP_StringLen xmpPacketLen,
+ WXMP_Result * result );
+
+extern void WXMPFiles_CanPutXMP_1 ( XMPFilesRef xmpFilesRef,
+ XMPMetaRef xmpRef, // ! Only one of the XMP object or packet are passed.
+ XMP_StringPtr xmpPacket,
+ XMP_StringLen xmpPacketLen,
+ WXMP_Result * result );
+
+extern void WXMPFiles_SetDefaultProgressCallback_1 ( XMP_ProgressReportWrapper wrapperproc,
+ XMP_ProgressReportProc clientProc,
+ void * context,
+ float interval,
+ XMP_Bool sendStartStop,
+ WXMP_Result * result );
+
+extern void WXMPFiles_SetProgressCallback_1 ( XMPFilesRef xmpFilesRef,
+ XMP_ProgressReportWrapper wrapperproc,
+ XMP_ProgressReportProc clientProc,
+ void * context,
+ float interval,
+ XMP_Bool sendStartStop,
+ WXMP_Result * result );
+
+// -------------------------------------------------------------------------------------------------
+
+extern void WXMPFiles_SetDefaultErrorCallback_1 ( XMPFiles_ErrorCallbackWrapper wrapperProc,
+ XMPFiles_ErrorCallbackProc clientProc,
+ void * context,
+ XMP_Uns32 limit,
+ WXMP_Result * wResult );
+
+extern void WXMPFiles_SetErrorCallback_1 ( XMPFilesRef xmpRef,
+ XMPFiles_ErrorCallbackWrapper wrapperProc,
+ XMPFiles_ErrorCallbackProc clientProc,
+ void * context,
+ XMP_Uns32 limit,
+ WXMP_Result * wResult );
+
+extern void WXMPFiles_ResetErrorCallbackLimit_1 ( XMPFilesRef xmpRef,
+ XMP_Uns32 limit,
+ WXMP_Result * wResult );
+
+// =================================================================================================
+
+#if __cplusplus
+}
+#endif
+
+#endif // __WXMPFiles_hpp__
diff --git a/gpr/source/lib/xmp_core/public/include/client-glue/WXMPIterator.hpp b/gpr/source/lib/xmp_core/public/include/client-glue/WXMPIterator.hpp
new file mode 100644
index 0000000..e40a1d4
--- /dev/null
+++ b/gpr/source/lib/xmp_core/public/include/client-glue/WXMPIterator.hpp
@@ -0,0 +1,74 @@
+#if ! __WXMPIterator_hpp__
+#define __WXMPIterator_hpp__ 1
+
+// =================================================================================================
+// Copyright 2002 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.
+// =================================================================================================
+
+#include "client-glue/WXMP_Common.hpp"
+
+#if __cplusplus
+extern "C" {
+#endif
+
+// =================================================================================================
+
+#define zXMPIterator_PropCTor_1(xmpRef,schemaNS,propName,options) \
+ WXMPIterator_PropCTor_1 ( xmpRef, schemaNS, propName, options, &wResult );
+
+#define zXMPIterator_TableCTor_1(schemaNS,propName,options) \
+ WXMPIterator_TableCTor_1 ( schemaNS, propName, options, &wResult );
+
+
+#define zXMPIterator_Next_1(schemaNS,propPath,propValue,options,SetClientString) \
+ WXMPIterator_Next_1 ( this->iterRef, schemaNS, propPath, propValue, options, SetClientString, &wResult );
+
+#define zXMPIterator_Skip_1(options) \
+ WXMPIterator_Skip_1 ( this->iterRef, options, &wResult );
+
+// -------------------------------------------------------------------------------------------------
+
+extern void
+XMP_PUBLIC WXMPIterator_PropCTor_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_OptionBits options,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPIterator_TableCTor_1 ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_OptionBits options,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPIterator_IncrementRefCount_1 ( XMPIteratorRef iterRef );
+
+extern void
+XMP_PUBLIC WXMPIterator_DecrementRefCount_1 ( XMPIteratorRef iterRef );
+
+extern void
+XMP_PUBLIC WXMPIterator_Next_1 ( XMPIteratorRef iterRef,
+ void * schemaNS,
+ void * propPath,
+ void * propValue,
+ XMP_OptionBits * options,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPIterator_Skip_1 ( XMPIteratorRef iterRef,
+ XMP_OptionBits options,
+ WXMP_Result * wResult );
+
+// =================================================================================================
+
+#if __cplusplus
+} /* extern "C" */
+#endif
+
+#endif // __WXMPIterator_hpp__
diff --git a/gpr/source/lib/xmp_core/public/include/client-glue/WXMPMeta.hpp b/gpr/source/lib/xmp_core/public/include/client-glue/WXMPMeta.hpp
new file mode 100644
index 0000000..361ad9d
--- /dev/null
+++ b/gpr/source/lib/xmp_core/public/include/client-glue/WXMPMeta.hpp
@@ -0,0 +1,621 @@
+#if ! __WXMPMeta_hpp__
+#define __WXMPMeta_hpp__ 1
+
+// =================================================================================================
+// Copyright 2002 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.
+// =================================================================================================
+
+#include "client-glue/WXMP_Common.hpp"
+
+#if __cplusplus
+extern "C" {
+#endif
+
+// =================================================================================================
+
+static XMP_Bool WrapErrorNotify ( XMPMeta_ErrorCallbackProc proc, void * context,
+ XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr message )
+{
+ bool ok;
+ try {
+ ok = (*proc) ( context, severity, cause, message );
+ } catch ( ... ) {
+ ok = false;
+ }
+ return ConvertBoolToXMP_Bool( ok );
+}
+
+// =================================================================================================
+
+#define zXMPMeta_GetVersionInfo_1(info) \
+ WXMPMeta_GetVersionInfo_1 ( info /* no wResult */ )
+
+#define zXMPMeta_Initialize_1() \
+ WXMPMeta_Initialize_1 ( &wResult )
+#define zXMPMeta_Terminate_1() \
+ WXMPMeta_Terminate_1 ( /* no wResult */ )
+
+#define zXMPMeta_CTor_1() \
+ WXMPMeta_CTor_1 ( &wResult )
+
+#define zXMPMeta_GetGlobalOptions_1() \
+ WXMPMeta_GetGlobalOptions_1 ( &wResult )
+
+#define zXMPMeta_SetGlobalOptions_1(options) \
+ WXMPMeta_SetGlobalOptions_1 ( options, &wResult )
+
+#define zXMPMeta_DumpNamespaces_1(outProc,refCon) \
+ WXMPMeta_DumpNamespaces_1 ( outProc, refCon, &wResult )
+
+#define zXMPMeta_RegisterNamespace_1(namespaceURI,suggestedPrefix,actualPrefix,SetClientString) \
+ WXMPMeta_RegisterNamespace_1 ( namespaceURI, suggestedPrefix, actualPrefix, SetClientString, &wResult )
+
+#define zXMPMeta_GetNamespacePrefix_1(namespaceURI,namespacePrefix,SetClientString) \
+ WXMPMeta_GetNamespacePrefix_1 ( namespaceURI, namespacePrefix, SetClientString, &wResult )
+
+#define zXMPMeta_GetNamespaceURI_1(namespacePrefix,namespaceURI,SetClientString) \
+ WXMPMeta_GetNamespaceURI_1 ( namespacePrefix, namespaceURI, SetClientString, &wResult )
+
+#define zXMPMeta_DeleteNamespace_1(namespaceURI) \
+ WXMPMeta_DeleteNamespace_1 ( namespaceURI, &wResult )
+
+#define zXMPMeta_GetProperty_1(schemaNS,propName,propValue,options,SetClientString) \
+ WXMPMeta_GetProperty_1 ( this->xmpRef, schemaNS, propName, propValue, options, SetClientString, &wResult )
+
+#define zXMPMeta_GetArrayItem_1(schemaNS,arrayName,itemIndex,itemValue,options,SetClientString) \
+ WXMPMeta_GetArrayItem_1 ( this->xmpRef, schemaNS, arrayName, itemIndex, itemValue, options, SetClientString, &wResult )
+
+#define zXMPMeta_GetStructField_1(schemaNS,structName,fieldNS,fieldName,fieldValue,options,SetClientString) \
+ WXMPMeta_GetStructField_1 ( this->xmpRef, schemaNS, structName, fieldNS, fieldName, fieldValue, options, SetClientString, &wResult )
+
+#define zXMPMeta_GetQualifier_1(schemaNS,propName,qualNS,qualName,qualValue,options,SetClientString) \
+ WXMPMeta_GetQualifier_1 ( this->xmpRef, schemaNS, propName, qualNS, qualName, qualValue, options, SetClientString, &wResult )
+
+#define zXMPMeta_SetProperty_1(schemaNS,propName,propValue,options) \
+ WXMPMeta_SetProperty_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult )
+
+#define zXMPMeta_SetArrayItem_1(schemaNS,arrayName,itemIndex,itemValue,options) \
+ WXMPMeta_SetArrayItem_1 ( this->xmpRef, schemaNS, arrayName, itemIndex, itemValue, options, &wResult )
+
+#define zXMPMeta_AppendArrayItem_1(schemaNS,arrayName,arrayOptions,itemValue,options) \
+ WXMPMeta_AppendArrayItem_1 ( this->xmpRef, schemaNS, arrayName, arrayOptions, itemValue, options, &wResult )
+
+#define zXMPMeta_SetStructField_1(schemaNS,structName,fieldNS,fieldName,fieldValue,options) \
+ WXMPMeta_SetStructField_1 ( this->xmpRef, schemaNS, structName, fieldNS, fieldName, fieldValue, options, &wResult )
+
+#define zXMPMeta_SetQualifier_1(schemaNS,propName,qualNS,qualName,qualValue,options) \
+ WXMPMeta_SetQualifier_1 ( this->xmpRef, schemaNS, propName, qualNS, qualName, qualValue, options, &wResult )
+
+#define zXMPMeta_DeleteProperty_1(schemaNS,propName) \
+ WXMPMeta_DeleteProperty_1 ( this->xmpRef, schemaNS, propName, &wResult )
+
+#define zXMPMeta_DeleteArrayItem_1(schemaNS,arrayName,itemIndex) \
+ WXMPMeta_DeleteArrayItem_1 ( this->xmpRef, schemaNS, arrayName, itemIndex, &wResult )
+
+#define zXMPMeta_DeleteStructField_1(schemaNS,structName,fieldNS,fieldName) \
+ WXMPMeta_DeleteStructField_1 ( this->xmpRef, schemaNS, structName, fieldNS, fieldName, &wResult )
+
+#define zXMPMeta_DeleteQualifier_1(schemaNS,propName,qualNS,qualName) \
+ WXMPMeta_DeleteQualifier_1 ( this->xmpRef, schemaNS, propName, qualNS, qualName, &wResult )
+
+#define zXMPMeta_DoesPropertyExist_1(schemaNS,propName) \
+ WXMPMeta_DoesPropertyExist_1 ( this->xmpRef, schemaNS, propName, &wResult )
+
+#define zXMPMeta_DoesArrayItemExist_1(schemaNS,arrayName,itemIndex) \
+ WXMPMeta_DoesArrayItemExist_1 ( this->xmpRef, schemaNS, arrayName, itemIndex, &wResult )
+
+#define zXMPMeta_DoesStructFieldExist_1(schemaNS,structName,fieldNS,fieldName) \
+ WXMPMeta_DoesStructFieldExist_1 ( this->xmpRef, schemaNS, structName, fieldNS, fieldName, &wResult )
+
+#define zXMPMeta_DoesQualifierExist_1(schemaNS,propName,qualNS,qualName) \
+ WXMPMeta_DoesQualifierExist_1 ( this->xmpRef, schemaNS, propName, qualNS, qualName, &wResult )
+
+#define zXMPMeta_GetLocalizedText_1(schemaNS,altTextName,genericLang,specificLang,clientLang,clientValue,options,SetClientString) \
+ WXMPMeta_GetLocalizedText_1 ( this->xmpRef, schemaNS, altTextName, genericLang, specificLang, clientLang, clientValue, options, SetClientString, &wResult )
+
+#define zXMPMeta_SetLocalizedText_1(schemaNS,altTextName,genericLang,specificLang,itemValue,options) \
+ WXMPMeta_SetLocalizedText_1 ( this->xmpRef, schemaNS, altTextName, genericLang, specificLang, itemValue, options, &wResult )
+
+#define zXMPMeta_DeleteLocalizedText_1(schemaNS,altTextName,genericLang,specificLang) \
+ WXMPMeta_DeleteLocalizedText_1 ( this->xmpRef, schemaNS, altTextName, genericLang, specificLang, &wResult )
+#define zXMPMeta_GetProperty_Bool_1(schemaNS,propName,propValue,options) \
+ WXMPMeta_GetProperty_Bool_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult )
+
+#define zXMPMeta_GetProperty_Int_1(schemaNS,propName,propValue,options) \
+ WXMPMeta_GetProperty_Int_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult )
+
+#define zXMPMeta_GetProperty_Int64_1(schemaNS,propName,propValue,options) \
+ WXMPMeta_GetProperty_Int64_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult )
+
+#define zXMPMeta_GetProperty_Float_1(schemaNS,propName,propValue,options) \
+ WXMPMeta_GetProperty_Float_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult )
+
+#define zXMPMeta_GetProperty_Date_1(schemaNS,propName,propValue,options) \
+ WXMPMeta_GetProperty_Date_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult )
+
+#define zXMPMeta_SetProperty_Bool_1(schemaNS,propName,propValue,options) \
+ WXMPMeta_SetProperty_Bool_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult )
+
+#define zXMPMeta_SetProperty_Int_1(schemaNS,propName,propValue,options) \
+ WXMPMeta_SetProperty_Int_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult )
+
+#define zXMPMeta_SetProperty_Int64_1(schemaNS,propName,propValue,options) \
+ WXMPMeta_SetProperty_Int64_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult )
+
+#define zXMPMeta_SetProperty_Float_1(schemaNS,propName,propValue,options) \
+ WXMPMeta_SetProperty_Float_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult )
+
+#define zXMPMeta_SetProperty_Date_1(schemaNS,propName,propValue,options) \
+ WXMPMeta_SetProperty_Date_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult )
+
+#define zXMPMeta_GetObjectName_1(objName,SetClientString) \
+ WXMPMeta_GetObjectName_1 ( this->xmpRef, objName, SetClientString, &wResult )
+
+#define zXMPMeta_SetObjectName_1(name) \
+ WXMPMeta_SetObjectName_1 ( this->xmpRef, name, &wResult )
+
+#define zXMPMeta_GetObjectOptions_1() \
+ WXMPMeta_GetObjectOptions_1 ( this->xmpRef, &wResult )
+
+#define zXMPMeta_SetObjectOptions_1(options) \
+ WXMPMeta_SetObjectOptions_1 ( this->xmpRef, options, &wResult )
+
+#define zXMPMeta_Sort_1() \
+ WXMPMeta_Sort_1 ( this->xmpRef, &wResult )
+
+#define zXMPMeta_Erase_1() \
+ WXMPMeta_Erase_1 ( this->xmpRef, &wResult )
+
+#define zXMPMeta_Clone_1(options) \
+ WXMPMeta_Clone_1 ( this->xmpRef, options, &wResult )
+
+#define zXMPMeta_CountArrayItems_1(schemaNS,arrayName) \
+ WXMPMeta_CountArrayItems_1 ( this->xmpRef, schemaNS, arrayName, &wResult )
+
+#define zXMPMeta_DumpObject_1(outProc,refCon) \
+ WXMPMeta_DumpObject_1 ( this->xmpRef, outProc, refCon, &wResult )
+
+#define zXMPMeta_ParseFromBuffer_1(buffer,bufferSize,options) \
+ WXMPMeta_ParseFromBuffer_1 ( this->xmpRef, buffer, bufferSize, options, &wResult )
+
+#define zXMPMeta_SerializeToBuffer_1(pktString,options,padding,newline,indent,baseIndent,SetClientString) \
+ WXMPMeta_SerializeToBuffer_1 ( this->xmpRef, pktString, options, padding, newline, indent, baseIndent, SetClientString, &wResult )
+
+#define zXMPMeta_SetDefaultErrorCallback_1(proc,context,limit) \
+ WXMPMeta_SetDefaultErrorCallback_1 ( WrapErrorNotify, proc, context, limit, &wResult )
+
+#define zXMPMeta_SetErrorCallback_1(proc,context,limit) \
+ WXMPMeta_SetErrorCallback_1 ( this->xmpRef, WrapErrorNotify, proc, context, limit, &wResult )
+
+#define zXMPMeta_ResetErrorCallbackLimit_1(limit) \
+ WXMPMeta_ResetErrorCallbackLimit_1 ( this->xmpRef, limit, &wResult )
+
+// =================================================================================================
+
+extern void
+XMP_PUBLIC WXMPMeta_GetVersionInfo_1 ( XMP_VersionInfo * info );
+
+extern void
+XMP_PUBLIC WXMPMeta_Initialize_1 ( WXMP_Result * wResult );
+extern void
+XMP_PUBLIC WXMPMeta_Terminate_1();
+
+// -------------------------------------------------------------------------------------------------
+
+extern void
+XMP_PUBLIC WXMPMeta_CTor_1 ( WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPMeta_IncrementRefCount_1 ( XMPMetaRef xmpRef );
+
+extern void
+XMP_PUBLIC WXMPMeta_DecrementRefCount_1 ( XMPMetaRef xmpRef );
+
+// -------------------------------------------------------------------------------------------------
+
+extern void
+XMP_PUBLIC WXMPMeta_GetGlobalOptions_1 ( WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPMeta_SetGlobalOptions_1 ( XMP_OptionBits options,
+ WXMP_Result * wResult );
+
+// -------------------------------------------------------------------------------------------------
+
+extern void
+XMP_PUBLIC WXMPMeta_DumpNamespaces_1 ( XMP_TextOutputProc outProc,
+ void * refCon,
+ WXMP_Result * wResult );
+
+// -------------------------------------------------------------------------------------------------
+
+extern void
+XMP_PUBLIC WXMPMeta_RegisterNamespace_1 ( XMP_StringPtr namespaceURI,
+ XMP_StringPtr suggestedPrefix,
+ void * actualPrefix,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPMeta_GetNamespacePrefix_1 ( XMP_StringPtr namespaceURI,
+ void * namespacePrefix,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPMeta_GetNamespaceURI_1 ( XMP_StringPtr namespacePrefix,
+ void * namespaceURI,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPMeta_DeleteNamespace_1 ( XMP_StringPtr namespaceURI,
+ WXMP_Result * wResult );
+
+// -------------------------------------------------------------------------------------------------
+
+extern void
+XMP_PUBLIC WXMPMeta_GetProperty_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ void * propValue,
+ XMP_OptionBits * options,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult ) /* const */ ;
+
+extern void
+XMP_PUBLIC WXMPMeta_GetArrayItem_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex,
+ void * itemValue,
+ XMP_OptionBits * options,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult ) /* const */ ;
+
+extern void
+XMP_PUBLIC WXMPMeta_GetStructField_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ void * fieldValue,
+ XMP_OptionBits * options,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult ) /* const */ ;
+
+extern void
+XMP_PUBLIC WXMPMeta_GetQualifier_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName,
+ void * qualValue,
+ XMP_OptionBits * options,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult ) /* const */ ;
+
+// -------------------------------------------------------------------------------------------------
+
+extern void
+XMP_PUBLIC WXMPMeta_SetProperty_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr propValue,
+ XMP_OptionBits options,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPMeta_SetArrayItem_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex,
+ XMP_StringPtr itemValue,
+ XMP_OptionBits options,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPMeta_AppendArrayItem_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_OptionBits arrayOptions,
+ XMP_StringPtr itemValue,
+ XMP_OptionBits options,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPMeta_SetStructField_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ XMP_StringPtr fieldValue,
+ XMP_OptionBits options,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPMeta_SetQualifier_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName,
+ XMP_StringPtr qualValue,
+ XMP_OptionBits options,
+ WXMP_Result * wResult );
+
+// -------------------------------------------------------------------------------------------------
+
+extern void
+XMP_PUBLIC WXMPMeta_DeleteProperty_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPMeta_DeleteArrayItem_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPMeta_DeleteStructField_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPMeta_DeleteQualifier_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName,
+ WXMP_Result * wResult );
+
+// -------------------------------------------------------------------------------------------------
+
+extern void
+XMP_PUBLIC WXMPMeta_DoesPropertyExist_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ WXMP_Result * wResult ) /* const */ ;
+
+extern void
+XMP_PUBLIC WXMPMeta_DoesArrayItemExist_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex,
+ WXMP_Result * wResult ) /* const */ ;
+
+extern void
+XMP_PUBLIC WXMPMeta_DoesStructFieldExist_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ WXMP_Result * wResult ) /* const */ ;
+
+extern void
+XMP_PUBLIC WXMPMeta_DoesQualifierExist_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName,
+ WXMP_Result * wResult ) /* const */ ;
+
+// -------------------------------------------------------------------------------------------------
+
+extern void
+XMP_PUBLIC WXMPMeta_GetLocalizedText_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr altTextName,
+ XMP_StringPtr genericLang,
+ XMP_StringPtr specificLang,
+ void * clientLang,
+ void * clientValue,
+ XMP_OptionBits * options,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult ) /* const */ ;
+
+extern void
+XMP_PUBLIC WXMPMeta_SetLocalizedText_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr altTextName,
+ XMP_StringPtr genericLang,
+ XMP_StringPtr specificLang,
+ XMP_StringPtr itemValue,
+ XMP_OptionBits options,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPMeta_DeleteLocalizedText_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr altTextName,
+ XMP_StringPtr genericLang,
+ XMP_StringPtr specificLang,
+ WXMP_Result * wResult );
+
+// -------------------------------------------------------------------------------------------------
+
+extern void
+XMP_PUBLIC WXMPMeta_GetProperty_Bool_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_Bool * propValue,
+ XMP_OptionBits * options,
+ WXMP_Result * wResult ) /* const */ ;
+
+extern void
+XMP_PUBLIC WXMPMeta_GetProperty_Int_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_Int32 * propValue,
+ XMP_OptionBits * options,
+ WXMP_Result * wResult ) /* const */ ;
+
+extern void
+XMP_PUBLIC WXMPMeta_GetProperty_Int64_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_Int64 * propValue,
+ XMP_OptionBits * options,
+ WXMP_Result * wResult ) /* const */ ;
+
+extern void
+XMP_PUBLIC WXMPMeta_GetProperty_Float_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ double * propValue,
+ XMP_OptionBits * options,
+ WXMP_Result * wResult ) /* const */ ;
+
+extern void
+XMP_PUBLIC WXMPMeta_GetProperty_Date_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_DateTime * propValue,
+ XMP_OptionBits * options,
+ WXMP_Result * wResult ) /* const */ ;
+
+extern void
+XMP_PUBLIC WXMPMeta_SetProperty_Bool_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_Bool propValue,
+ XMP_OptionBits options,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPMeta_SetProperty_Int_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_Int32 propValue,
+ XMP_OptionBits options,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPMeta_SetProperty_Int64_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_Int64 propValue,
+ XMP_OptionBits options,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPMeta_SetProperty_Float_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ double propValue,
+ XMP_OptionBits options,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPMeta_SetProperty_Date_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ const XMP_DateTime & propValue,
+ XMP_OptionBits options,
+ WXMP_Result * wResult );
+
+// -------------------------------------------------------------------------------------------------
+
+extern void
+XMP_PUBLIC WXMPMeta_GetObjectName_1 ( XMPMetaRef xmpRef,
+ void * objName,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult ) /* const */ ;
+
+extern void
+XMP_PUBLIC WXMPMeta_SetObjectName_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr name,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPMeta_GetObjectOptions_1 ( XMPMetaRef xmpRef,
+ WXMP_Result * wResult ) /* const */ ;
+
+extern void
+XMP_PUBLIC WXMPMeta_SetObjectOptions_1 ( XMPMetaRef xmpRef,
+ XMP_OptionBits options,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPMeta_Sort_1 ( XMPMetaRef xmpRef,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPMeta_Erase_1 ( XMPMetaRef xmpRef,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPMeta_Clone_1 ( XMPMetaRef xmpRef,
+ XMP_OptionBits options,
+ WXMP_Result * wResult ) /* const */ ;
+
+extern void
+XMP_PUBLIC WXMPMeta_CountArrayItems_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ WXMP_Result * wResult ) /* const */ ;
+
+extern void
+XMP_PUBLIC WXMPMeta_DumpObject_1 ( XMPMetaRef xmpRef,
+ XMP_TextOutputProc outProc,
+ void * refCon,
+ WXMP_Result * wResult ) /* const */ ;
+
+// -------------------------------------------------------------------------------------------------
+
+extern void
+XMP_PUBLIC WXMPMeta_ParseFromBuffer_1 ( XMPMetaRef xmpRef,
+ XMP_StringPtr buffer,
+ XMP_StringLen bufferSize,
+ XMP_OptionBits options,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPMeta_SerializeToBuffer_1 ( XMPMetaRef xmpRef,
+ void * pktString,
+ XMP_OptionBits options,
+ XMP_StringLen padding,
+ XMP_StringPtr newline,
+ XMP_StringPtr indent,
+ XMP_Index baseIndent,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult ) /* const */ ;
+
+// -------------------------------------------------------------------------------------------------
+
+extern void
+XMP_PUBLIC WXMPMeta_SetDefaultErrorCallback_1 ( XMPMeta_ErrorCallbackWrapper wrapperProc,
+ XMPMeta_ErrorCallbackProc clientProc,
+ void * context,
+ XMP_Uns32 limit,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPMeta_SetErrorCallback_1 ( XMPMetaRef xmpRef,
+ XMPMeta_ErrorCallbackWrapper wrapperProc,
+ XMPMeta_ErrorCallbackProc clientProc,
+ void * context,
+ XMP_Uns32 limit,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPMeta_ResetErrorCallbackLimit_1 ( XMPMetaRef xmpRef,
+ XMP_Uns32 limit,
+ WXMP_Result * wResult );
+
+// =================================================================================================
+
+#if __cplusplus
+} /* extern "C" */
+#endif
+
+#endif // __WXMPMeta_hpp__
diff --git a/gpr/source/lib/xmp_core/public/include/client-glue/WXMPUtils.hpp b/gpr/source/lib/xmp_core/public/include/client-glue/WXMPUtils.hpp
new file mode 100644
index 0000000..3c96b83
--- /dev/null
+++ b/gpr/source/lib/xmp_core/public/include/client-glue/WXMPUtils.hpp
@@ -0,0 +1,315 @@
+#if ! __WXMPUtils_hpp__
+#define __WXMPUtils_hpp__ 1
+
+// =================================================================================================
+// Copyright 2002 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.
+// =================================================================================================
+
+#include "client-glue/WXMP_Common.hpp"
+
+#if __cplusplus
+extern "C" {
+#endif
+
+// =================================================================================================
+
+#define zXMPUtils_ComposeArrayItemPath_1(schemaNS,arrayName,itemIndex,itemPath,SetClientString) \
+ WXMPUtils_ComposeArrayItemPath_1 ( schemaNS, arrayName, itemIndex, itemPath, SetClientString, &wResult );
+
+#define zXMPUtils_ComposeStructFieldPath_1(schemaNS,structName,fieldNS,fieldName,fieldPath,SetClientString) \
+ WXMPUtils_ComposeStructFieldPath_1 ( schemaNS, structName, fieldNS, fieldName, fieldPath, SetClientString, &wResult );
+
+#define zXMPUtils_ComposeQualifierPath_1(schemaNS,propName,qualNS,qualName,qualPath,SetClientString) \
+ WXMPUtils_ComposeQualifierPath_1 ( schemaNS, propName, qualNS, qualName, qualPath, SetClientString, &wResult );
+
+#define zXMPUtils_ComposeLangSelector_1(schemaNS,arrayName,langName,selPath,SetClientString) \
+ WXMPUtils_ComposeLangSelector_1 ( schemaNS, arrayName, langName, selPath, SetClientString, &wResult );
+
+#define zXMPUtils_ComposeFieldSelector_1(schemaNS,arrayName,fieldNS,fieldName,fieldValue,selPath,SetClientString) \
+ WXMPUtils_ComposeFieldSelector_1 ( schemaNS, arrayName, fieldNS, fieldName, fieldValue, selPath, SetClientString, &wResult );
+
+#define zXMPUtils_ConvertFromBool_1(binValue,strValue,SetClientString) \
+ WXMPUtils_ConvertFromBool_1 ( binValue, strValue, SetClientString, &wResult );
+
+#define zXMPUtils_ConvertFromInt_1(binValue,format,strValue,SetClientString) \
+ WXMPUtils_ConvertFromInt_1 ( binValue, format, strValue, SetClientString, &wResult );
+
+#define zXMPUtils_ConvertFromInt64_1(binValue,format,strValue,SetClientString) \
+ WXMPUtils_ConvertFromInt64_1 ( binValue, format, strValue, SetClientString, &wResult );
+
+#define zXMPUtils_ConvertFromFloat_1(binValue,format,strValue,SetClientString) \
+ WXMPUtils_ConvertFromFloat_1 ( binValue, format, strValue, SetClientString, &wResult );
+
+#define zXMPUtils_ConvertFromDate_1(binValue,strValue,SetClientString) \
+ WXMPUtils_ConvertFromDate_1 ( binValue, strValue, SetClientString, &wResult );
+
+#define zXMPUtils_ConvertToBool_1(strValue) \
+ WXMPUtils_ConvertToBool_1 ( strValue, &wResult );
+
+#define zXMPUtils_ConvertToInt_1(strValue) \
+ WXMPUtils_ConvertToInt_1 ( strValue, &wResult );
+
+#define zXMPUtils_ConvertToInt64_1(strValue) \
+ WXMPUtils_ConvertToInt64_1 ( strValue, &wResult );
+
+#define zXMPUtils_ConvertToFloat_1(strValue) \
+ WXMPUtils_ConvertToFloat_1 ( strValue, &wResult );
+
+#define zXMPUtils_ConvertToDate_1(strValue,binValue) \
+ WXMPUtils_ConvertToDate_1 ( strValue, binValue, &wResult );
+
+#define zXMPUtils_CurrentDateTime_1(time) \
+ WXMPUtils_CurrentDateTime_1 ( time, &wResult );
+
+#define zXMPUtils_SetTimeZone_1(time) \
+ WXMPUtils_SetTimeZone_1 ( time, &wResult );
+
+#define zXMPUtils_ConvertToUTCTime_1(time) \
+ WXMPUtils_ConvertToUTCTime_1 ( time, &wResult );
+
+#define zXMPUtils_ConvertToLocalTime_1(time) \
+ WXMPUtils_ConvertToLocalTime_1 ( time, &wResult );
+
+#define zXMPUtils_CompareDateTime_1(left,right) \
+ WXMPUtils_CompareDateTime_1 ( left, right, &wResult );
+
+#define zXMPUtils_EncodeToBase64_1(rawStr,rawLen,encodedStr,SetClientString) \
+ WXMPUtils_EncodeToBase64_1 ( rawStr, rawLen, encodedStr, SetClientString, &wResult );
+
+#define zXMPUtils_DecodeFromBase64_1(encodedStr,encodedLen,rawStr,SetClientString) \
+ WXMPUtils_DecodeFromBase64_1 ( encodedStr, encodedLen, rawStr, SetClientString, &wResult );
+
+#define zXMPUtils_PackageForJPEG_1(xmpObj,stdStr,extStr,digestStr,SetClientString) \
+ WXMPUtils_PackageForJPEG_1 ( xmpObj, stdStr, extStr, digestStr, SetClientString, &wResult );
+
+#define zXMPUtils_MergeFromJPEG_1(fullXMP,extendedXMP) \
+ WXMPUtils_MergeFromJPEG_1 ( fullXMP, extendedXMP, &wResult );
+
+#define zXMPUtils_CatenateArrayItems_1(xmpObj,schemaNS,arrayName,separator,quotes,options,catedStr,SetClientString) \
+ WXMPUtils_CatenateArrayItems_1 ( xmpObj, schemaNS, arrayName, separator, quotes, options, catedStr, SetClientString, &wResult );
+
+#define zXMPUtils_SeparateArrayItems_1(xmpObj,schemaNS,arrayName,options,catedStr) \
+ WXMPUtils_SeparateArrayItems_1 ( xmpObj, schemaNS, arrayName, options, catedStr, &wResult );
+
+#define zXMPUtils_ApplyTemplate_1(workingXMP,templateXMP,actions) \
+ WXMPUtils_ApplyTemplate_1 ( workingXMP, templateXMP, actions, &wResult );
+
+#define zXMPUtils_RemoveProperties_1(xmpObj,schemaNS,propName,options) \
+ WXMPUtils_RemoveProperties_1 ( xmpObj, schemaNS, propName, options, &wResult );
+
+#define zXMPUtils_DuplicateSubtree_1(source,dest,sourceNS,sourceRoot,destNS,destRoot,options) \
+ WXMPUtils_DuplicateSubtree_1 ( source, dest, sourceNS, sourceRoot, destNS, destRoot, options, &wResult );
+
+// =================================================================================================
+
+extern void
+XMP_PUBLIC WXMPUtils_ComposeArrayItemPath_1 ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_Index itemIndex,
+ void * itemPath,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPUtils_ComposeStructFieldPath_1 ( XMP_StringPtr schemaNS,
+ XMP_StringPtr structName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ void * fieldPath,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPUtils_ComposeQualifierPath_1 ( XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_StringPtr qualNS,
+ XMP_StringPtr qualName,
+ void * qualPath,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPUtils_ComposeLangSelector_1 ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr langName,
+ void * selPath,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPUtils_ComposeFieldSelector_1 ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr fieldNS,
+ XMP_StringPtr fieldName,
+ XMP_StringPtr fieldValue,
+ void * selPath,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult );
+
+// -------------------------------------------------------------------------------------------------
+
+extern void
+XMP_PUBLIC WXMPUtils_ConvertFromBool_1 ( XMP_Bool binValue,
+ void * strValue,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPUtils_ConvertFromInt_1 ( XMP_Int32 binValue,
+ XMP_StringPtr format,
+ void * strValue,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPUtils_ConvertFromInt64_1 ( XMP_Int64 binValue,
+ XMP_StringPtr format,
+ void * strValue,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPUtils_ConvertFromFloat_1 ( double binValue,
+ XMP_StringPtr format,
+ void * strValue,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPUtils_ConvertFromDate_1 ( const XMP_DateTime & binValue,
+ void * strValue,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult );
+
+// -------------------------------------------------------------------------------------------------
+
+extern void
+XMP_PUBLIC WXMPUtils_ConvertToBool_1 ( XMP_StringPtr strValue,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPUtils_ConvertToInt_1 ( XMP_StringPtr strValue,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPUtils_ConvertToInt64_1 ( XMP_StringPtr strValue,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPUtils_ConvertToFloat_1 ( XMP_StringPtr strValue,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPUtils_ConvertToDate_1 ( XMP_StringPtr strValue,
+ XMP_DateTime * binValue,
+ WXMP_Result * wResult );
+
+// -------------------------------------------------------------------------------------------------
+
+extern void
+XMP_PUBLIC WXMPUtils_CurrentDateTime_1 ( XMP_DateTime * time,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPUtils_SetTimeZone_1 ( XMP_DateTime * time,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPUtils_ConvertToUTCTime_1 ( XMP_DateTime * time,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPUtils_ConvertToLocalTime_1 ( XMP_DateTime * time,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPUtils_CompareDateTime_1 ( const XMP_DateTime & left,
+ const XMP_DateTime & right,
+ WXMP_Result * wResult );
+
+// -------------------------------------------------------------------------------------------------
+
+extern void
+XMP_PUBLIC WXMPUtils_EncodeToBase64_1 ( XMP_StringPtr rawStr,
+ XMP_StringLen rawLen,
+ void * encodedStr,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPUtils_DecodeFromBase64_1 ( XMP_StringPtr encodedStr,
+ XMP_StringLen encodedLen,
+ void * rawStr,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult );
+
+// -------------------------------------------------------------------------------------------------
+
+extern void
+XMP_PUBLIC WXMPUtils_PackageForJPEG_1 ( XMPMetaRef xmpObj,
+ void * stdStr,
+ void * extStr,
+ void * digestStr,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPUtils_MergeFromJPEG_1 ( XMPMetaRef fullXMP,
+ XMPMetaRef extendedXMP,
+ WXMP_Result * wResult );
+
+// -------------------------------------------------------------------------------------------------
+
+extern void
+XMP_PUBLIC WXMPUtils_CatenateArrayItems_1 ( XMPMetaRef xmpObj,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr separator,
+ XMP_StringPtr quotes,
+ XMP_OptionBits options,
+ void * catedStr,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPUtils_SeparateArrayItems_1 ( XMPMetaRef xmpObj,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_OptionBits options,
+ XMP_StringPtr catedStr,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPUtils_ApplyTemplate_1 ( XMPMetaRef workingXMP,
+ XMPMetaRef templateXMP,
+ XMP_OptionBits options,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPUtils_RemoveProperties_1 ( XMPMetaRef xmpObj,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_OptionBits options,
+ WXMP_Result * wResult );
+
+extern void
+XMP_PUBLIC WXMPUtils_DuplicateSubtree_1 ( XMPMetaRef source,
+ XMPMetaRef dest,
+ XMP_StringPtr sourceNS,
+ XMP_StringPtr sourceRoot,
+ XMP_StringPtr destNS,
+ XMP_StringPtr destRoot,
+ XMP_OptionBits options,
+ WXMP_Result * wResult );
+
+// =================================================================================================
+
+#if __cplusplus
+} /* extern "C" */
+#endif
+
+#endif // __WXMPUtils_hpp__
diff --git a/gpr/source/lib/xmp_core/public/include/client-glue/WXMP_Common.hpp b/gpr/source/lib/xmp_core/public/include/client-glue/WXMP_Common.hpp
new file mode 100644
index 0000000..97fb9fc
--- /dev/null
+++ b/gpr/source/lib/xmp_core/public/include/client-glue/WXMP_Common.hpp
@@ -0,0 +1,128 @@
+#if ! __WXMP_Common_hpp__
+#define __WXMP_Common_hpp__ 1
+
+// =================================================================================================
+// Copyright 2002 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.
+// =================================================================================================
+
+#ifndef XMP_Inline
+ #if TXMP_EXPAND_INLINE
+ #define XMP_Inline inline
+ #else
+ #define XMP_Inline /* not inline */
+ #endif
+#endif
+
+#define XMP_CTorDTorIntro(Class) template <class tStringObj> XMP_Inline Class<tStringObj>
+#define XMP_MethodIntro(Class,ResultType) template <class tStringObj> XMP_Inline ResultType Class<tStringObj>
+
+typedef void (* SetClientStringProc) ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen );
+typedef void (* SetClientStringVectorProc) ( void * clientPtr, XMP_StringPtr * arrayPtr, XMP_Uns32 stringCount );
+
+struct WXMP_Result {
+ XMP_StringPtr errMessage;
+ void * ptrResult;
+ double floatResult;
+ XMP_Uns64 int64Result;
+ XMP_Uns32 int32Result;
+ WXMP_Result() : errMessage(0),ptrResult(NULL),floatResult(0),int64Result(0),int32Result(0){};
+};
+
+#if __cplusplus
+extern "C" {
+#endif
+
+#define PropagateException(res) \
+ if ( res.errMessage != 0 ) throw XMP_Error ( res.int32Result, res.errMessage );
+
+#ifndef XMP_TraceClientCalls
+ #define XMP_TraceClientCalls 0
+ #define XMP_TraceClientCallsToFile 0
+#endif
+
+#if ! XMP_TraceClientCalls
+ #define InvokeCheck(WCallProto) \
+ WXMP_Result wResult; \
+ WCallProto; \
+ PropagateException ( wResult )
+#else
+ extern FILE * xmpClientLog;
+ #define InvokeCheck(WCallProto) \
+ WXMP_Result wResult; \
+ fprintf ( xmpClientLog, "WXMP calling: %s\n", #WCallProto ); fflush ( xmpClientLog ); \
+ WCallProto; \
+ if ( wResult.errMessage == 0 ) { \
+ fprintf ( xmpClientLog, "WXMP back, no error\n" ); fflush ( xmpClientLog ); \
+ } else { \
+ fprintf ( xmpClientLog, "WXMP back, error: %s\n", wResult.errMessage ); fflush ( xmpClientLog ); \
+ } \
+ PropagateException ( wResult )
+#endif
+
+// =================================================================================================
+
+#define WrapNoCheckVoid(WCallProto) \
+ WCallProto;
+
+#define WrapCheckVoid(WCallProto) \
+ InvokeCheck(WCallProto);
+
+#define WrapCheckMetaRef(result,WCallProto) \
+ InvokeCheck(WCallProto); \
+ XMPMetaRef result = XMPMetaRef(wResult.ptrResult)
+
+#define WrapCheckIterRef(result,WCallProto) \
+ InvokeCheck(WCallProto); \
+ XMPIteratorRef result = XMPIteratorRef(wResult.ptrResult)
+
+#define WrapCheckDocOpsRef(result,WCallProto) \
+ InvokeCheck(WCallProto); \
+ XMPDocOpsRef result = XMPDocOpsRef(wResult.ptrResult)
+
+#define WrapCheckBool(result,WCallProto) \
+ InvokeCheck(WCallProto); \
+ bool result = bool(wResult.int32Result)
+
+#define WrapCheckTriState(result,WCallProto) \
+ InvokeCheck(WCallProto); \
+ XMP_TriState result = XMP_TriState(wResult.int32Result)
+
+#define WrapCheckOptions(result,WCallProto) \
+ InvokeCheck(WCallProto); \
+ XMP_OptionBits result = XMP_OptionBits(wResult.int32Result)
+
+#define WrapCheckStatus(result,WCallProto) \
+ InvokeCheck(WCallProto); \
+ XMP_Status result = XMP_Status(wResult.int32Result)
+
+#define WrapCheckIndex(result,WCallProto) \
+ InvokeCheck(WCallProto); \
+ XMP_Index result = XMP_Index(wResult.int32Result)
+
+#define WrapCheckInt32(result,WCallProto) \
+ InvokeCheck(WCallProto); \
+ XMP_Int32 result = wResult.int32Result
+
+#define WrapCheckInt64(result,WCallProto) \
+ InvokeCheck(WCallProto); \
+ XMP_Int64 result = wResult.int64Result
+
+#define WrapCheckFloat(result,WCallProto) \
+ InvokeCheck(WCallProto); \
+ double result = wResult.floatResult
+
+#define WrapCheckFormat(result,WCallProto) \
+ InvokeCheck(WCallProto); \
+ XMP_FileFormat result = wResult.int32Result
+
+// =================================================================================================
+
+#if __cplusplus
+} // extern "C"
+#endif
+
+#endif // __WXMP_Common_hpp__