// =================================================================================================
// 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 "XMPFiles/source/NativeMetadataSupport/IReconcile.h"
#include "XMPFiles/source/NativeMetadataSupport/MetadataSet.h"
#include "XMPFiles/source/NativeMetadataSupport/IMetadata.h"
#include "XMPFiles/source/FormatSupport/Reconcile_Impl.hpp"
//-----------------------------------------------------------------------------
//
// WAVEReconcile::importNativeToXMP(...)
//
// Purpose: [static] Import native metadata container into XMP.
// This method is supposed to import all native metadata values
// that are listed in the property table from the IMetadata
// instance to the SXMPMeta instance.
//
//-----------------------------------------------------------------------------
bool IReconcile::importNativeToXMP( SXMPMeta& outXMP, const IMetadata& nativeMeta, const MetadataPropertyInfo* propertyInfo, bool xmpPriority )
{
bool changed = false;
std::string xmpValue;
XMP_Uns32 index = 0;
do
{
if( propertyInfo[index].mXMPSchemaNS != NULL )
{
bool xmpPropertyExists = false;
//
// need to know if the XMP property exists either
// if a priority needs to be considered or if
// the value isn't set by a native value
//
switch( propertyInfo[index].mXMPType )
{
case kXMPType_Simple:
{
xmpPropertyExists = outXMP.DoesPropertyExist( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName );
}
break;
case kXMPType_Localized:
{
std::string actualLang;
bool result = outXMP.GetLocalizedText( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName, "" , "x-default" , &actualLang, NULL, NULL );
// If existing and the appropriate one
xmpPropertyExists = result && (actualLang == "x-default");
}
break;
case kXMPType_Array:
case kXMPType_OrderedArray:
{
xmpPropertyExists = outXMP.DoesArrayItemExist( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName, 1 );
}
break;
default:
{
XMP_Throw( "Unknown XMP data type", kXMPErr_InternalFailure );
}
break;
}
//
// import native property if there is no priority or
// the XMP property doesn't exist yet
//
if( ! propertyInfo[index].mConsiderPriority || ! xmpPriority || ! xmpPropertyExists )
{
//
// if the native property exists
//
if( nativeMeta.valueExists( propertyInfo[index].mMetadataID ) )
{
//
// prepare native property value for XMP
// depending on the native data type
//
xmpValue.erase();
switch( propertyInfo[index].mNativeType )
{
case kNativeType_Str:
{
xmpValue = nativeMeta.getValue<std::string>( propertyInfo[index].mMetadataID );
}
break;
case kNativeType_StrASCII:
{
convertToASCII( nativeMeta.getValue<std::string>( propertyInfo[index].mMetadataID ), xmpValue );
}
break;
case kNativeType_StrLocal:
{
ReconcileUtils::NativeToUTF8( nativeMeta.getValue<std::string>( propertyInfo[index].mMetadataID ), xmpValue );
}
break;
case kNativeType_StrUTF8:
{
ReconcileUtils::NativeToUTF8( nativeMeta.getValue<std::string>( propertyInfo[index].mMetadataID ), xmpValue );
}
break;
case kNativeType_Uns64:
{
SXMPUtils::ConvertFromInt64( nativeMeta.getValue<XMP_Uns64>( propertyInfo[index].mMetadataID ), "%llu", &xmpValue );
}
break;
case kNativeType_Uns32:
{
SXMPUtils::ConvertFromInt( nativeMeta.getValue<XMP_Uns32>( propertyInfo[index].mMetadataID ), "%lu", &xmpValue );
}
break;
case kNativeType_Int32:
{
SXMPUtils::ConvertFromInt( nativeMeta.getValue<XMP_Int32>( propertyInfo[index].mMetadataID ), 0 /* default format */, &xmpValue );
}
break;
case kNativeType_Uns16:
{
SXMPUtils::ConvertFromInt( nativeMeta.getValue<XMP_Uns16>( propertyInfo[index].mMetadataID ), "%lu", &xmpValue );
}
break;
case kNativeType_Bool:
{
SXMPUtils::ConvertFromBool( nativeMeta.getValue<bool>( propertyInfo[index].mMetadataID ), &xmpValue );
}
break;
default:
{
XMP_Throw( "Unknown native data type", kXMPErr_InternalFailure );
}
break;
};
if( ! xmpValue.empty() )
{
//
// set the new XMP value depending on the
// XMP data type
//
switch( propertyInfo[index].mXMPType )
{
case kXMPType_Localized:
{
outXMP.SetLocalizedText( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName, NULL, "x-default", xmpValue.c_str() );
}
break;
case kXMPType_Array:
{
// Overwrite any existing array
outXMP.DeleteProperty( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName );
outXMP.AppendArrayItem( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName, kXMP_PropArrayIsUnordered, xmpValue.c_str(), kXMP_NoOptions );
}
break;
case kXMPType_OrderedArray:
{
// Overwrite any existing array
outXMP.DeleteProperty( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName );
outXMP.AppendArrayItem( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName, kXMP_PropArrayIsOrdered, xmpValue.c_str(), kXMP_NoOptions );
}
break;
default:
{
outXMP.SetProperty( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName, xmpValue.c_str() );
}
break;
}
changed = true;
}
}
else if( propertyInfo[index].mDeleteXMPIfNoNative && xmpPropertyExists )
{
//
// native value doesn't exists, delete the XMP property
//
outXMP.DeleteProperty( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName );
changed = xmpPropertyExists;
}
}
}
index++;
} while( propertyInfo[index].mXMPSchemaNS != NULL );
return changed;
}
//-----------------------------------------------------------------------------
//
// WAVEReconcile::exportXMPToNative(...)
//
// Purpose: [static] Export XMP values to native metadata container.
// This method is supposed to export all native metadata values
// that are listed in the property table from the XMP container
// to the IMetadata instance.
//
//-----------------------------------------------------------------------------
bool IReconcile::exportXMPToNative( IMetadata& outNativeMeta, SXMPMeta& inXMP, const MetadataPropertyInfo* propertyInfo, PropertyList * propertiesExportedSuccessfully /*= NULL*/ )
{
std::string xmpValue;
XMP_Uns32 index = 0;
do
{
if( propertyInfo[index].mXMPSchemaNS != NULL && propertyInfo[index].mExportPolicy != kExport_Never )
{
bool xmpPropertyExists = false;
//
// get value from XMP depending on
// the XMP data type
//
switch( propertyInfo[index].mXMPType )
{
case kXMPType_Localized:
{
std::string lang;
xmpPropertyExists = inXMP.GetLocalizedText( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName, NULL, "x-default", &lang, &xmpValue, 0 );
}
break;
case kXMPType_Array:
case kXMPType_OrderedArray:
{
// TOTRACK currently only the first array item is used
if( inXMP.CountArrayItems( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName ) > 0 )
{
xmpPropertyExists = inXMP.GetArrayItem( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName, 1, &xmpValue, 0 );
}
}
break;
default:
{
xmpPropertyExists = inXMP.GetProperty( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName, &xmpValue, 0 );
}
break;
}
//
// convert xmp value and set native property
// depending on the native data type and the export policy
//
if( xmpPropertyExists &&
( propertyInfo[index].mExportPolicy != kExport_InjectOnly ||
! outNativeMeta.valueExists( propertyInfo[index].mMetadataID ) ) )
{
switch( propertyInfo[index].mNativeType )
{
case kNativeType_StrASCII:
{
std::string ascii;
convertToASCII( xmpValue, ascii );
outNativeMeta.setValue<std::string>( propertyInfo[index].mMetadataID, ascii );
if ( propertiesExportedSuccessfully )
propertiesExportedSuccessfully->push_back( std::make_pair( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName ) );
}
break;
case kNativeType_Str:
case kNativeType_StrUTF8:
{
outNativeMeta.setValue<std::string>( propertyInfo[index].mMetadataID, xmpValue );
if ( propertiesExportedSuccessfully )
propertiesExportedSuccessfully->push_back( std::make_pair( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName ) );
}
break;
case kNativeType_StrLocal:
{
std::string value;
try
{
ReconcileUtils::UTF8ToLocal( xmpValue.c_str(), xmpValue.size(), &value );
outNativeMeta.setValue<std::string>( propertyInfo[index].mMetadataID, value );
if ( propertiesExportedSuccessfully )
propertiesExportedSuccessfully->push_back( std::make_pair( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName ) );
}
catch( XMP_Error& e )
{
if( e.GetID() != kXMPErr_Unavailable )
{
// rethrow exception if it wasn't caused
// by missing encoding functionality (UNIX)
throw e;
}
}
}
break;
case kNativeType_Uns64:
{
XMP_Int64 value = 0;
bool error = false;
try
{
value = SXMPUtils::ConvertToInt64( xmpValue );
}
catch( XMP_Error& e )
{
if ( e.GetID() != kXMPErr_BadParam )
{
// rethrow exception if it wasn't caused by an
// invalid parameter for the conversion
throw e;
}
error = true;
}
// Only write the value if it could be converted to a number and has a positive value
if( ! error && value >= 0 )
{
outNativeMeta.setValue<XMP_Uns64>( propertyInfo[index].mMetadataID, static_cast<XMP_Uns64>(value) );
if ( propertiesExportedSuccessfully )
propertiesExportedSuccessfully->push_back( std::make_pair( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName ) );
}
}
break;
case kNativeType_Uns32:
{
XMP_Int32 value = 0;
bool error = false;
try
{
value = SXMPUtils::ConvertToInt( xmpValue );
}
catch( XMP_Error& e )
{
if ( e.GetID() != kXMPErr_BadParam )
{
// rethrow exception if it wasn't caused by an
// invalid parameter for the conversion
throw e;
}
error = true;
}
// Only write the value if it could be converted to a number and has a positive value
if( ! error && value >= 0 )
{
outNativeMeta.setValue<XMP_Uns32>( propertyInfo[index].mMetadataID, static_cast<XMP_Uns32>(value) );
if ( propertiesExportedSuccessfully )
propertiesExportedSuccessfully->push_back( std::make_pair( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName ) );
}
}
break;
case kNativeType_Int32:
{
XMP_Int32 value = 0;
bool error = false;
try
{
value = SXMPUtils::ConvertToInt( xmpValue );
}
catch( XMP_Error& e )
{
if ( e.GetID() != kXMPErr_BadParam )
{
// rethrow exception if it wasn't caused by an
// invalid parameter for the conversion
throw e;
}
error = true;
}
// Only write the value if it could be converted to a number
if( ! error )
{
outNativeMeta.setValue<XMP_Int32>( propertyInfo[index].mMetadataID, static_cast<XMP_Int32>(value) );
if ( propertiesExportedSuccessfully )
propertiesExportedSuccessfully->push_back( std::make_pair( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName ) );
}
}
break;
case kNativeType_Uns16:
{
XMP_Int32 value = 0;
bool error = false;
try
{
value = SXMPUtils::ConvertToInt( xmpValue );
}
catch( XMP_Error& e )
{
if ( e.GetID() != kXMPErr_BadParam )
{
// rethrow exception if it wasn't caused by an
// invalid parameter for the conversion
throw e;
}
error = true;
}
// Only write the value if it could be converted to a number and has a positive value
if( ! error && value >= 0 )
{
outNativeMeta.setValue<XMP_Uns16>( propertyInfo[index].mMetadataID, static_cast<XMP_Uns16>(value) );
if ( propertiesExportedSuccessfully )
propertiesExportedSuccessfully->push_back( std::make_pair( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName ) );
}
}
break;
case kNativeType_Bool:
{
bool value;
bool error = false;
try
{
value = SXMPUtils::ConvertToBool( xmpValue );
}
catch( XMP_Error& e )
{
if ( e.GetID() != kXMPErr_BadParam )
{
// rethrow exception if it wasn't caused by an
// invalid parameter for the conversion
throw e;
}
error = true;
}
// Only write the value if it could be converted to a number and has a positive value
if( ! error )
{
outNativeMeta.setValue<bool>( propertyInfo[index].mMetadataID, value );
if ( propertiesExportedSuccessfully )
propertiesExportedSuccessfully->push_back( std::make_pair( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName ) );
}
}
break;
default:
{
XMP_Throw( "Unknown native data type", kXMPErr_InternalFailure );
}
break;
}
}
else
{
if( propertyInfo[index].mExportPolicy == kExport_Always )
{
//
// delete native value if the corresponding XMP value doesn't exists
//
outNativeMeta.deleteValue( propertyInfo[index].mMetadataID );
}
}
}
index++;
} while( propertyInfo[index].mXMPSchemaNS != NULL );
return outNativeMeta.hasChanged();
}
//-----------------------------------------------------------------------------
//
// IReconcile::convertToASCII(...)
//
// Purpose: Converts input string to an ascii output string
// - terminates at first 0
// - replaces all non ascii with 0x3F ('?')
//
//-----------------------------------------------------------------------------
void IReconcile::convertToASCII( const std::string& input, std::string& output )
{
output.erase();
output.reserve( input.length() );
bool isUTF8 = ReconcileUtils::IsUTF8( input.c_str(), input.length() );
XMP_StringPtr iter = input.c_str();
for ( XMP_Uns32 i=0; i < input.length(); i++ )
{
XMP_Uns8 c = (XMP_Uns8) iter[i];
if ( c == 0 ) // early 0 termination, leave.
break;
if ( c > 127 ) // uft-8 multi-byte sequence.
{
if ( isUTF8 ) // skip all high bytes
{
// how many bytes in this ?
if ( c >= 0xC2 && c <= 0xDF )
i+=1; // 2-byte sequence
else if ( c >= 0xE0 && c <= 0xEF )
i+=2; // 3-byte sequence
else if ( c >= 0xF0 && c <= 0xF4 )
i+=3; // 4-byte sequence
else
continue; //invalid sequence, look for next 'low' byte ..
} // thereafter and 'else': just append a question mark:
output.append( 1, '?' );
}
else // regular valid ascii. 1 byte.
{
output.append( 1, iter[i] );
}
}
} // convertToASCII