Esempio n. 1
0
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
Esempio n. 2
0
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]", static_cast<unsigned long>(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
Esempio n. 3
0
static void
SerializeAsRDF ( const XMPMeta & xmpObj,
				 XMP_VarString & headStr,	// Everything up to the padding.
				 XMP_VarString & tailStr,	// Everything after the padding.
				 XMP_OptionBits	 options,
				 XMP_StringPtr	 newline,
				 XMP_StringPtr	 indentStr,
				 XMP_Index		 baseIndent )
{
	const size_t treeNameLen = xmpObj.tree.name.size();
	const size_t indentLen   = strlen ( indentStr );

	// First estimate the worst case space and reserve room in the output string. This optimization
	// avoids reallocating and copying the output as it grows. The initial count does not look at
	// the values of properties, so it does not account for character entities, e.g. &#xA; for newline.
	// Since there can be a lot of these in things like the base 64 encoding of a large thumbnail,
	// inflate the count by 1/4 (easy to do) to accommodate.
	
	// *** Need to include estimate for alias comments.
	
	size_t outputLen = 2 * (strlen(kPacketHeader) + strlen(kRDF_XMPMetaStart) + strlen(kRDF_RDFStart) + 3*baseIndent*indentLen);
	
	for ( size_t schemaNum = 0, schemaLim = xmpObj.tree.children.size(); schemaNum < schemaLim; ++schemaNum ) {
		const XMP_Node * currSchema = xmpObj.tree.children[schemaNum];
		outputLen += 2*(baseIndent+2)*indentLen + strlen(kRDF_SchemaStart) + treeNameLen + strlen(kRDF_SchemaEnd) + 2;
		outputLen += EstimateRDFSize ( currSchema, baseIndent+2, indentLen );
	}
	
	outputLen += (outputLen >> 2);	// Inflate by 1/4, an empirical fudge factor.
	
	// Now generate the RDF into the head string as UTF-8.
	
	XMP_Index level;
	
	headStr.erase();
	headStr.reserve ( outputLen );
	
	// 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 "\">";
		headStr += newline;
	}

	// Write the rdf:RDF start tag.
	for ( level = baseIndent+1; level > 0; --level ) headStr += indentStr;
	headStr += kRDF_RDFStart;
	headStr += newline;
	
	// Write all of the properties.
	if ( options & kXMP_UseCompactFormat ) {
		SerializeCompactRDFSchemas ( xmpObj.tree, headStr, newline, indentStr, baseIndent );
	} else {
		if ( xmpObj.tree.children.size() > 0 ) {
			for ( size_t schemaNum = 0, schemaLim = xmpObj.tree.children.size(); schemaNum < schemaLim; ++schemaNum ) {
				const XMP_Node * currSchema = xmpObj.tree.children[schemaNum];
				SerializePrettyRDFSchema ( xmpObj.tree.name, currSchema, headStr, options, newline, indentStr, baseIndent );
			}
		} else {
			for ( XMP_Index level = baseIndent+2; level > 0; --level ) headStr += indentStr;
			headStr += kRDF_SchemaStart;	// Special case an empty XMP object.
			headStr += '"';
			headStr += xmpObj.tree.name;
			headStr += "\"/>";
			headStr += newline;
		}
	}

	// Write the rdf:RDF end tag.
	for ( level = baseIndent+1; level > 0; --level ) headStr += indentStr;
	headStr += kRDF_RDFEnd;
	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';
	}
	
	// ! This assert is just a performance check, to see if the reserve was enough.
	// *** XMP_Assert ( headStr.size() <= outputLen );
	// *** Don't use an assert. Think of some way to track this without risk of aborting the client.
	
}	// SerializeAsRDF
static void
SerializeAsRDF ( const XMPMeta & xmpObj,
				 XMP_VarString & headStr,	// Everything up to the padding.
				 XMP_VarString & tailStr,	// Everything after the padding.
				 XMP_OptionBits	 options,
				 XMP_StringPtr	 newline,
				 XMP_StringPtr	 indentStr,
				 XMP_Index		 baseIndent )
{
	const size_t treeNameLen = xmpObj.tree.name.size();
	const size_t indentLen   = strlen ( indentStr );

	// First estimate the worst case space and reserve room in the output string. This optimization
	// avoids reallocating and copying the output as it grows. The initial count does not look at
	// the values of properties, so it does not account for character entities, e.g. &#xA; for newline.
	// Since there can be a lot of these in things like the base 64 encoding of a large thumbnail,
	// inflate the count by 1/4 (easy to do) to accommodate.
	
	// *** Need to include estimate for alias comments.
	
	size_t outputLen = 2 * (strlen(kPacketHeader) + strlen(kRDF_XMPMetaStart) + strlen(kRDF_RDFStart) + 3*baseIndent*indentLen);

	for ( size_t schemaNum = 0, schemaLim = xmpObj.tree.children.size(); schemaNum < schemaLim; ++schemaNum ) {
		const XMP_Node * currSchema = xmpObj.tree.children[schemaNum];
		outputLen += 2*(baseIndent+2)*indentLen + strlen(kRDF_SchemaStart) + treeNameLen + strlen(kRDF_SchemaEnd) + 2;
		outputLen += EstimateRDFSize ( currSchema, baseIndent+2, indentLen );
	}
	
	outputLen += (outputLen >> 2);	// Inflate by 1/4, an empirical fudge factor.
	
	// Now generate the RDF into the head string as UTF-8.
	
	XMP_Index level;
	
	std::string rdfstring;
	headStr.erase();
	rdfstring.reserve ( outputLen );

	// Write the rdf:RDF start tag.
	rdfstring += kRDF_RDFStart;
	rdfstring += newline;
	
	// Write all of the properties.
	if ( options & kXMP_UseCompactFormat ) {
		SerializeCompactRDFSchemas ( xmpObj.tree, rdfstring, newline, indentStr, baseIndent );
	} else {
		bool useCanonicalRDF = XMP_OptionIsSet ( options, kXMP_UseCanonicalFormat );
		SerializeCanonicalRDFSchemas ( xmpObj.tree, rdfstring, newline, indentStr, baseIndent, useCanonicalRDF );
	}

	// Write the rdf:RDF end tag.
	for ( level = baseIndent+1; level > 0; --level ) rdfstring += indentStr;
	rdfstring += kRDF_RDFEnd;
	// Write the packet header PI.
	if ( ! (options & kXMP_OmitPacketWrapper) ) {
		for ( level = baseIndent; level > 0; --level ) headStr += indentStr;
		headStr += kPacketHeader;
		headStr += newline;
	}

	// Write the xmpmeta element's start tag.
	if ( ! (options & kXMP_OmitXMPMetaElement) ) {
		for ( level = baseIndent; level > 0; --level ) headStr += indentStr;
		headStr += kRDF_XMPMetaStart;
		headStr += kXMPCore_VersionMessage  "\"";
		std::string digestStr;
		unsigned char digestBin [16];
		if (options & kXMP_IncludeRDFHash)
		{
			std::string hashrdf;	
			MD5_CTX    context;
			MD5Init ( &context );
			MD5Update ( &context, (XMP_Uns8*)rdfstring.c_str(), (unsigned int)rdfstring.size() );
			MD5Final ( digestBin, &context );
			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;
	}
Esempio n. 5
0
/* 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