diff options
author | luxagraf <sng@luxagraf.net> | 2023-06-15 15:58:59 -0500 |
---|---|---|
committer | luxagraf <sng@luxagraf.net> | 2023-06-15 15:58:59 -0500 |
commit | ab987e10f154f5536bb8fd936ae0966e909fa969 (patch) | |
tree | 9de5076f38b71ececb1bc94f8d9d19170898d603 /gpr/source/lib/xmp_core |
added all my scriptssynced/master
Diffstat (limited to 'gpr/source/lib/xmp_core')
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 (&, <, >, ", ', 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 "". +// 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 ("	" or "	") and upper or lower case ("
" or "
"). +// 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 += """; + } else if ( ch == '<' ) { + outputStr += "<"; + } else if ( ch == '>' ) { + outputStr += ">"; + } else { + XMP_Assert ( ch == '&' ); + outputStr += "&"; + } + + } + + ++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. 
 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__ |