| /*****************************************************************************/ |
| // Copyright 2006-2012 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. |
| /*****************************************************************************/ |
| |
| /* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_xmp_sdk.cpp#4 $ */ |
| /* $DateTime: 2012/09/05 12:31:51 $ */ |
| /* $Change: 847652 $ */ |
| /* $Author: tknoll $ */ |
| |
| /*****************************************************************************/ |
| |
| #include "dng_xmp_sdk.h" |
| |
| #include "dng_auto_ptr.h" |
| #include "dng_assertions.h" |
| #include "dng_exceptions.h" |
| #include "dng_flags.h" |
| #include "dng_host.h" |
| #include "dng_memory.h" |
| #include "dng_string.h" |
| #include "dng_string_list.h" |
| #include "dng_utils.h" |
| |
| /*****************************************************************************/ |
| |
| #if qMacOS |
| #ifndef MAC_ENV |
| #define MAC_ENV 1 |
| #endif |
| #endif |
| |
| #if qWinOS |
| #ifndef WIN_ENV |
| #define WIN_ENV 1 |
| #endif |
| #endif |
| |
| #include <new> |
| #include <string> |
| |
| #define TXMP_STRING_TYPE std::string |
| |
| #define XMP_INCLUDE_XMPFILES qDNGXMPFiles |
| |
| #define XMP_StaticBuild 1 |
| |
| #include "XMP.incl_cpp" |
| |
| /*****************************************************************************/ |
| |
| const char *XMP_NS_TIFF = "http://ns.adobe.com/tiff/1.0/"; |
| const char *XMP_NS_EXIF = "http://ns.adobe.com/exif/1.0/"; |
| const char *XMP_NS_PHOTOSHOP = "http://ns.adobe.com/photoshop/1.0/"; |
| const char *XMP_NS_XAP = "http://ns.adobe.com/xap/1.0/"; |
| const char *XMP_NS_XAP_RIGHTS = "http://ns.adobe.com/xap/1.0/rights/"; |
| const char *XMP_NS_DC = "http://purl.org/dc/elements/1.1/"; |
| const char *XMP_NS_XMP_NOTE = "http://ns.adobe.com/xmp/note/"; |
| const char *XMP_NS_MM = "http://ns.adobe.com/xap/1.0/mm/"; |
| |
| const char *XMP_NS_CRS = "http://ns.adobe.com/camera-raw-settings/1.0/"; |
| const char *XMP_NS_CRSS = "http://ns.adobe.com/camera-raw-saved-settings/1.0/"; |
| const char *XMP_NS_AUX = "http://ns.adobe.com/exif/1.0/aux/"; |
| |
| const char *XMP_NS_LCP = "http://ns.adobe.com/photoshop/1.0/camera-profile"; |
| |
| const char *XMP_NS_IPTC = "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/"; |
| const char *XMP_NS_IPTC_EXT = "http://iptc.org/std/Iptc4xmpExt/2008-02-29/"; |
| |
| const char *XMP_NS_CRX = "http://ns.adobe.com/lightroom-settings-experimental/1.0/"; |
| |
| const char *XMP_NS_DNG = "http://ns.adobe.com/dng/1.0/"; |
| |
| /******************************************************************************/ |
| |
| #define CATCH_XMP(routine, fatal)\ |
| \ |
| catch (std::bad_alloc &)\ |
| {\ |
| DNG_REPORT ("Info: XMP " routine " threw memory exception");\ |
| ThrowMemoryFull ();\ |
| }\ |
| \ |
| catch (XMP_Error &error)\ |
| {\ |
| const char *errMessage = error.GetErrMsg ();\ |
| if (errMessage && strlen (errMessage) <= 128)\ |
| {\ |
| char errBuffer [256];\ |
| sprintf (errBuffer, "Info: XMP " routine " threw '%s' exception", errMessage);\ |
| DNG_REPORT ( errBuffer);\ |
| }\ |
| else\ |
| {\ |
| DNG_REPORT ("Info: XMP " routine " threw unnamed exception");\ |
| }\ |
| if (fatal) ThrowProgramError ();\ |
| }\ |
| \ |
| catch (...)\ |
| {\ |
| DNG_REPORT ("Info: XMP " routine " threw unknown exception");\ |
| if (fatal) ThrowProgramError ();\ |
| } |
| |
| /*****************************************************************************/ |
| |
| class dng_xmp_private |
| { |
| |
| public: |
| |
| SXMPMeta *fMeta; |
| |
| dng_xmp_private () |
| : fMeta (NULL) |
| { |
| } |
| |
| dng_xmp_private (const dng_xmp_private &xmp); |
| |
| ~dng_xmp_private () |
| { |
| if (fMeta) |
| { |
| delete fMeta; |
| } |
| } |
| |
| private: |
| |
| // Hidden assignment operator. |
| |
| dng_xmp_private & operator= (const dng_xmp_private &xmp); |
| |
| }; |
| |
| /*****************************************************************************/ |
| |
| dng_xmp_private::dng_xmp_private (const dng_xmp_private &xmp) |
| |
| : fMeta (NULL) |
| |
| { |
| |
| if (xmp.fMeta) |
| { |
| |
| fMeta = new SXMPMeta (xmp.fMeta->Clone (0)); |
| |
| if (!fMeta) |
| { |
| ThrowMemoryFull (); |
| } |
| |
| } |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| dng_xmp_sdk::dng_xmp_sdk () |
| |
| : fPrivate (NULL) |
| |
| { |
| |
| fPrivate = new dng_xmp_private; |
| |
| if (!fPrivate) |
| { |
| ThrowMemoryFull (); |
| } |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| dng_xmp_sdk::dng_xmp_sdk (const dng_xmp_sdk &sdk) |
| |
| : fPrivate (NULL) |
| |
| { |
| |
| fPrivate = new dng_xmp_private (*sdk.fPrivate); |
| |
| if (!fPrivate) |
| { |
| ThrowMemoryFull (); |
| } |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| dng_xmp_sdk::~dng_xmp_sdk () |
| { |
| |
| if (fPrivate) |
| { |
| delete fPrivate; |
| } |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| static bool gInitializedXMP = false; |
| |
| /*****************************************************************************/ |
| |
| void dng_xmp_sdk::InitializeSDK (dng_xmp_namespace * extraNamespaces, |
| const char *software) |
| { |
| |
| if (!gInitializedXMP) |
| { |
| |
| try |
| { |
| |
| if (!SXMPMeta::Initialize ()) |
| { |
| ThrowProgramError (); |
| } |
| |
| // Register Lightroom beta settings namespace. |
| // We no longer read this but I don't want to cut it out this close |
| // to a release. [bruzenak] |
| |
| { |
| |
| TXMP_STRING_TYPE ss; |
| |
| SXMPMeta::RegisterNamespace (XMP_NS_CRX, |
| "crx", |
| &ss); |
| |
| } |
| |
| // Register CRSS snapshots namespace |
| |
| { |
| |
| TXMP_STRING_TYPE ss; |
| |
| SXMPMeta::RegisterNamespace (XMP_NS_CRSS, |
| "crss", |
| &ss); |
| |
| } |
| |
| // Register LCP (lens correction profiles) namespace |
| |
| { |
| |
| TXMP_STRING_TYPE ss; |
| |
| SXMPMeta::RegisterNamespace (XMP_NS_LCP, |
| "stCamera", |
| &ss); |
| |
| } |
| |
| // Register DNG format metadata namespace |
| |
| { |
| |
| TXMP_STRING_TYPE ss; |
| |
| SXMPMeta::RegisterNamespace (XMP_NS_DNG, |
| "dng", |
| &ss); |
| |
| } |
| |
| // Register extra namespaces. |
| |
| if (extraNamespaces != NULL) |
| { |
| |
| for (; extraNamespaces->fullName != NULL; ++extraNamespaces) |
| { |
| |
| TXMP_STRING_TYPE ss; |
| |
| SXMPMeta::RegisterNamespace (extraNamespaces->fullName, |
| extraNamespaces->shortName, |
| &ss); |
| |
| } |
| |
| } |
| |
| #if qDNGXMPFiles |
| |
| #if qLinux |
| if (!SXMPFiles::Initialize (kXMPFiles_IgnoreLocalText)) |
| #else |
| if (!SXMPFiles::Initialize ()) |
| #endif |
| { |
| ThrowProgramError (); |
| } |
| |
| #endif |
| |
| #if qDNGXMPDocOps |
| |
| if (software) |
| { |
| |
| SXMPDocOps::SetAppName (software); |
| |
| } |
| |
| #else |
| |
| (void) software; |
| |
| #endif |
| |
| } |
| |
| CATCH_XMP ("Initialization", true) |
| |
| gInitializedXMP = true; |
| |
| } |
| |
| } |
| |
| /******************************************************************************/ |
| |
| void dng_xmp_sdk::TerminateSDK () |
| { |
| |
| if (gInitializedXMP) |
| { |
| |
| try |
| { |
| |
| #if qDNGXMPFiles |
| |
| SXMPFiles::Terminate (); |
| |
| #endif |
| |
| SXMPMeta::Terminate (); |
| |
| } |
| |
| catch (...) |
| { |
| |
| } |
| |
| gInitializedXMP = false; |
| |
| } |
| |
| } |
| |
| /******************************************************************************/ |
| |
| bool dng_xmp_sdk::HasMeta () const |
| { |
| |
| if (fPrivate->fMeta) |
| { |
| |
| return true; |
| |
| } |
| |
| return false; |
| |
| } |
| |
| /******************************************************************************/ |
| |
| void dng_xmp_sdk::ClearMeta () |
| { |
| |
| if (HasMeta ()) |
| { |
| |
| delete fPrivate->fMeta; |
| |
| fPrivate->fMeta = NULL; |
| |
| } |
| |
| } |
| |
| /******************************************************************************/ |
| |
| void dng_xmp_sdk::MakeMeta () |
| { |
| |
| ClearMeta (); |
| |
| InitializeSDK (); |
| |
| try |
| { |
| |
| fPrivate->fMeta = new SXMPMeta; |
| |
| if (!fPrivate->fMeta) |
| { |
| |
| ThrowMemoryFull (); |
| |
| } |
| |
| } |
| |
| CATCH_XMP ("MakeMeta", true) |
| |
| } |
| |
| /******************************************************************************/ |
| |
| void dng_xmp_sdk::NeedMeta () |
| { |
| |
| if (!HasMeta ()) |
| { |
| |
| MakeMeta (); |
| |
| } |
| |
| } |
| |
| /******************************************************************************/ |
| |
| void * dng_xmp_sdk::GetPrivateMeta () |
| { |
| |
| NeedMeta (); |
| |
| return (void *) fPrivate->fMeta; |
| |
| } |
| |
| /******************************************************************************/ |
| |
| void dng_xmp_sdk::Parse (dng_host &host, |
| const char *buffer, |
| uint32 count) |
| { |
| |
| MakeMeta (); |
| |
| try |
| { |
| |
| try |
| { |
| |
| fPrivate->fMeta->ParseFromBuffer (buffer, count); |
| |
| } |
| |
| CATCH_XMP ("ParseFromBuffer", true) |
| |
| } |
| |
| catch (dng_exception &except) |
| { |
| |
| ClearMeta (); |
| |
| if (host.IsTransientError (except.ErrorCode ())) |
| { |
| |
| throw; |
| |
| } |
| |
| ThrowBadFormat (); |
| |
| } |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_xmp_sdk::AppendArrayItem (const char *ns, |
| const char *arrayName, |
| const char *itemValue, |
| bool isBag, |
| bool propIsStruct) |
| { |
| |
| NeedMeta(); |
| |
| try |
| { |
| |
| fPrivate->fMeta->AppendArrayItem (ns, |
| arrayName, |
| isBag ? kXMP_PropValueIsArray |
| : kXMP_PropArrayIsOrdered, |
| itemValue, |
| propIsStruct ? kXMP_PropValueIsStruct |
| : 0); |
| |
| } |
| CATCH_XMP ("AppendArrayItem", true ) |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| int32 dng_xmp_sdk::CountArrayItems (const char *ns, |
| const char *path) const |
| { |
| |
| if (HasMeta ()) |
| { |
| |
| try |
| { |
| |
| return fPrivate->fMeta->CountArrayItems (ns, path); |
| |
| } |
| |
| CATCH_XMP ("CountArrayItems", false) |
| |
| } |
| |
| return 0; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| bool dng_xmp_sdk::Exists (const char *ns, |
| const char *path) const |
| { |
| |
| if (HasMeta ()) |
| { |
| |
| try |
| { |
| |
| return fPrivate->fMeta->DoesPropertyExist (ns, path); |
| |
| } |
| |
| catch (...) |
| { |
| |
| // Does not exist... |
| |
| } |
| |
| } |
| |
| return false; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| bool dng_xmp_sdk::HasNameSpace (const char *ns) const |
| { |
| |
| bool result = false; |
| |
| if (HasMeta ()) |
| { |
| |
| try |
| { |
| |
| SXMPIterator iter (*fPrivate->fMeta, ns); |
| |
| TXMP_STRING_TYPE nsTemp; |
| TXMP_STRING_TYPE prop; |
| |
| if (iter.Next (&nsTemp, |
| &prop, |
| NULL, |
| NULL)) |
| { |
| |
| result = true; |
| |
| } |
| |
| } |
| |
| CATCH_XMP ("HasNameSpace", true) |
| |
| } |
| |
| return result; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_xmp_sdk::Remove (const char *ns, |
| const char *path) |
| { |
| |
| if (HasMeta ()) |
| { |
| |
| try |
| { |
| |
| fPrivate->fMeta->DeleteProperty (ns, path); |
| |
| } |
| |
| CATCH_XMP ("DeleteProperty", false) |
| |
| } |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_xmp_sdk::RemoveProperties (const char *ns) |
| { |
| |
| if (HasMeta ()) |
| { |
| |
| try |
| { |
| |
| SXMPUtils::RemoveProperties (fPrivate->fMeta, |
| ns, |
| NULL, |
| kXMPUtil_DoAllProperties); |
| |
| } |
| |
| catch (...) |
| { |
| |
| } |
| |
| } |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| bool dng_xmp_sdk::IsEmptyString (const char *ns, |
| const char *path) |
| { |
| |
| if (HasMeta ()) |
| { |
| |
| try |
| { |
| |
| TXMP_STRING_TYPE ss; |
| |
| XMP_OptionBits options = 0; |
| |
| if (fPrivate->fMeta->GetProperty (ns, |
| path, |
| &ss, |
| &options)) |
| { |
| |
| // Item must be simple. |
| |
| if (XMP_PropIsSimple (options)) |
| { |
| |
| // Check for null strings. |
| |
| return (ss.c_str () == 0 || |
| ss.c_str () [0] == 0); |
| |
| } |
| |
| } |
| |
| } |
| |
| CATCH_XMP ("IsEmptyString", false) |
| |
| } |
| |
| return false; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| bool dng_xmp_sdk::IsEmptyArray (const char *ns, |
| const char *path) |
| { |
| |
| if (HasMeta ()) |
| { |
| |
| try |
| { |
| |
| TXMP_STRING_TYPE ss; |
| |
| XMP_OptionBits options = 0; |
| |
| if (fPrivate->fMeta->GetProperty (ns, |
| path, |
| &ss, |
| &options)) |
| { |
| |
| if (XMP_PropIsArray (options)) |
| { |
| |
| if (fPrivate->fMeta->GetArrayItem (ns, |
| path, |
| 1, |
| &ss, |
| &options)) |
| { |
| |
| // If the first item is a null string... |
| |
| if (XMP_PropIsSimple (options)) |
| { |
| |
| if ((ss.c_str () == 0 || |
| ss.c_str () [0] == 0)) |
| { |
| |
| // And there is no second item. |
| |
| if (!fPrivate->fMeta->GetArrayItem (ns, |
| path, |
| 2, |
| &ss, |
| &options)) |
| { |
| |
| // Then we have an empty array. |
| |
| return true; |
| |
| } |
| |
| } |
| |
| } |
| |
| } |
| |
| else |
| { |
| |
| // Unable to get first item, so array is empty. |
| |
| return true; |
| |
| } |
| |
| } |
| |
| } |
| |
| } |
| |
| CATCH_XMP ("IsEmptyArray", false) |
| |
| } |
| |
| return false; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_xmp_sdk::ComposeArrayItemPath (const char *ns, |
| const char *arrayName, |
| int32 index, |
| dng_string &s) const |
| { |
| |
| try |
| { |
| |
| std::string ss; |
| |
| SXMPUtils::ComposeArrayItemPath (ns, arrayName, index, &ss); |
| |
| s.Set (ss.c_str ()); |
| |
| return; |
| |
| } |
| |
| CATCH_XMP ("ComposeArrayItemPath", true) |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_xmp_sdk::ComposeStructFieldPath (const char *ns, |
| const char *structName, |
| const char *fieldNS, |
| const char *fieldName, |
| dng_string &s) const |
| { |
| |
| try |
| { |
| |
| std::string ss; |
| |
| SXMPUtils::ComposeStructFieldPath (ns, |
| structName, |
| fieldNS, |
| fieldName, |
| &ss); |
| |
| s.Set (ss.c_str ()); |
| |
| return; |
| |
| } |
| |
| CATCH_XMP ("ComposeStructFieldPath", true) |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| bool dng_xmp_sdk::GetNamespacePrefix (const char *uri, |
| dng_string &s) const |
| { |
| |
| bool result = false; |
| |
| if (HasMeta ()) |
| { |
| |
| try |
| { |
| |
| std::string ss; |
| |
| fPrivate->fMeta->GetNamespacePrefix (uri, &ss); |
| |
| s.Set (ss.c_str ()); |
| |
| result = true; |
| |
| } |
| |
| CATCH_XMP ("GetNamespacePrefix", false) |
| |
| } |
| |
| return result; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| bool dng_xmp_sdk::GetString (const char *ns, |
| const char *path, |
| dng_string &s) const |
| { |
| |
| bool result = false; |
| |
| if (HasMeta ()) |
| { |
| |
| try |
| { |
| |
| TXMP_STRING_TYPE ss; |
| |
| if (fPrivate->fMeta->GetProperty (ns, path, &ss, NULL)) |
| { |
| |
| s.Set (ss.c_str ()); |
| |
| result = true; |
| |
| } |
| |
| } |
| |
| CATCH_XMP ("GetProperty", false) |
| |
| } |
| |
| return result; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_xmp_sdk::ValidateStringList (const char *ns, |
| const char *path) |
| { |
| |
| if (Exists (ns, path)) |
| { |
| |
| bool bogus = true; |
| |
| try |
| { |
| |
| XMP_Index index = 1; |
| |
| TXMP_STRING_TYPE ss; |
| |
| while (fPrivate->fMeta->GetArrayItem (ns, |
| path, |
| index++, |
| &ss, |
| NULL)) |
| { |
| |
| } |
| |
| bogus = false; |
| |
| } |
| |
| CATCH_XMP ("GetArrayItem", false) |
| |
| if (bogus) |
| { |
| |
| Remove (ns, path); |
| |
| } |
| |
| } |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| bool dng_xmp_sdk::GetStringList (const char *ns, |
| const char *path, |
| dng_string_list &list) const |
| { |
| |
| bool result = false; |
| |
| if (HasMeta ()) |
| { |
| |
| try |
| { |
| |
| XMP_Index index = 1; |
| |
| TXMP_STRING_TYPE ss; |
| |
| while (fPrivate->fMeta->GetArrayItem (ns, |
| path, |
| index++, |
| &ss, |
| NULL)) |
| { |
| |
| dng_string s; |
| |
| s.Set (ss.c_str ()); |
| |
| list.Append (s); |
| |
| result = true; |
| |
| } |
| |
| } |
| |
| CATCH_XMP ("GetArrayItem", false) |
| |
| } |
| |
| return result; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| bool dng_xmp_sdk::GetAltLangDefault (const char *ns, |
| const char *path, |
| dng_string &s) const |
| { |
| |
| bool result = false; |
| |
| if (HasMeta ()) |
| { |
| |
| try |
| { |
| |
| TXMP_STRING_TYPE ss; |
| |
| if (fPrivate->fMeta->GetLocalizedText (ns, |
| path, |
| "x-default", |
| "x-default", |
| NULL, |
| &ss, |
| NULL)) |
| { |
| |
| s.Set (ss.c_str ()); |
| |
| result = true; |
| |
| } |
| // |
| // Special Case: treat the following two representation equivalently. |
| // The first is an empty alt lang array; the second is an array with |
| // an empty item. It seems that xmp lib could be generating both under |
| // some circumstances! |
| // |
| // <dc:description> |
| // <rdf:Alt/> |
| // </dc:description> |
| // |
| // and |
| // |
| // <dc:description> |
| // <rdf:Alt> |
| // <rdf:li xml:lang="x-default"/> |
| // </rdf:Alt> |
| // </dc:description> |
| // |
| else if (fPrivate->fMeta->GetProperty (ns, |
| path, |
| &ss, |
| NULL)) |
| { |
| |
| if (ss.empty ()) |
| { |
| |
| s.Clear (); |
| |
| result = true; |
| |
| } |
| |
| } |
| |
| } |
| |
| CATCH_XMP ("GetLocalizedText", false) |
| |
| } |
| |
| return result; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| bool dng_xmp_sdk::GetStructField (const char *ns, |
| const char *path, |
| const char *fieldNS, |
| const char *fieldName, |
| dng_string &s) const |
| { |
| |
| bool result = false; |
| |
| if (HasMeta ()) |
| { |
| |
| try |
| { |
| |
| TXMP_STRING_TYPE ss; |
| |
| if (fPrivate->fMeta->GetStructField (ns, |
| path, |
| fieldNS, |
| fieldName, |
| &ss, |
| NULL)) |
| { |
| |
| s.Set (ss.c_str ()); |
| |
| result = true; |
| |
| } |
| |
| } |
| |
| CATCH_XMP ("GetStructField", false) |
| |
| } |
| |
| return result; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_xmp_sdk::Set (const char *ns, |
| const char *path, |
| const char *text) |
| { |
| |
| NeedMeta (); |
| |
| try |
| { |
| |
| fPrivate->fMeta->SetProperty (ns, path, text); |
| |
| return; |
| |
| } |
| |
| catch (...) |
| { |
| |
| // Failed for some reason. |
| |
| } |
| |
| // Remove existing value and try again. |
| |
| Remove (ns, path); |
| |
| try |
| { |
| |
| fPrivate->fMeta->SetProperty (ns, path, text); |
| |
| } |
| |
| CATCH_XMP ("SetProperty", true) |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_xmp_sdk::SetString (const char *ns, |
| const char *path, |
| const dng_string &s) |
| { |
| |
| dng_string ss (s); |
| |
| ss.SetLineEndings ('\n'); |
| |
| ss.StripLowASCII (); |
| |
| Set (ns, path, ss.Get ()); |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_xmp_sdk::SetStringList (const char *ns, |
| const char *path, |
| const dng_string_list &list, |
| bool isBag) |
| { |
| |
| // Remove any existing structure. |
| |
| Remove (ns, path); |
| |
| // If list is not empty, add the items. |
| |
| if (list.Count ()) |
| { |
| |
| NeedMeta (); |
| |
| for (uint32 index = 0; index < list.Count (); index++) |
| { |
| |
| dng_string s (list [index]); |
| |
| s.SetLineEndings ('\n'); |
| |
| s.StripLowASCII (); |
| |
| try |
| { |
| |
| fPrivate->fMeta->AppendArrayItem (ns, |
| path, |
| isBag ? kXMP_PropValueIsArray |
| : kXMP_PropArrayIsOrdered, |
| s.Get ()); |
| |
| } |
| |
| CATCH_XMP ("AppendArrayItem", true) |
| |
| } |
| |
| } |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_xmp_sdk::SetAltLangDefault (const char *ns, |
| const char *path, |
| const dng_string &s) |
| { |
| |
| NeedMeta (); |
| |
| Remove (ns, path); |
| |
| dng_string ss (s); |
| |
| ss.SetLineEndings ('\n'); |
| |
| ss.StripLowASCII (); |
| |
| try |
| { |
| |
| fPrivate->fMeta->SetLocalizedText (ns, |
| path, |
| "x-default", |
| "x-default", |
| ss.Get ()); |
| |
| } |
| |
| CATCH_XMP ("SetLocalizedText", true) |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_xmp_sdk::SetStructField (const char *ns, |
| const char *path, |
| const char *fieldNS, |
| const char *fieldName, |
| const char *text) |
| { |
| |
| NeedMeta (); |
| |
| try |
| { |
| |
| fPrivate->fMeta->SetStructField (ns, |
| path, |
| fieldNS, |
| fieldName, |
| text); |
| |
| } |
| |
| CATCH_XMP ("SetStructField", true) |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_xmp_sdk::DeleteStructField (const char *ns, |
| const char *structName, |
| const char *fieldNS, |
| const char *fieldName) |
| { |
| |
| if (HasMeta ()) |
| { |
| |
| try |
| { |
| |
| fPrivate->fMeta->DeleteStructField (ns, structName, fieldNS, fieldName); |
| |
| } |
| |
| catch (...) |
| { |
| |
| } |
| |
| } |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| dng_memory_block * dng_xmp_sdk::Serialize (dng_memory_allocator &allocator, |
| bool asPacket, |
| uint32 targetBytes, |
| uint32 padBytes, |
| bool forJPEG, |
| bool compact) const |
| { |
| |
| // The largest XMP packet you can embed in JPEG using normal methods: |
| |
| const uint32 kJPEG_XMP_Limit = 65504; |
| |
| if (HasMeta ()) |
| { |
| |
| TXMP_STRING_TYPE s; |
| |
| bool havePacket = false; |
| |
| // Note that the XMP lib is changing its default to compact format |
| // in the future, so the following line will need to change. |
| |
| uint32 formatOption = compact ? kXMP_UseCompactFormat : 0; |
| |
| if (asPacket && targetBytes) |
| { |
| |
| try |
| { |
| |
| fPrivate->fMeta->SerializeToBuffer (&s, |
| formatOption | kXMP_ExactPacketLength, |
| targetBytes, |
| "", |
| " "); |
| |
| havePacket = true; |
| |
| } |
| |
| catch (...) |
| { |
| |
| // Most likely the packet cannot fit in the target |
| // byte count. So try again without the limit. |
| |
| } |
| |
| } |
| |
| if (!havePacket) |
| { |
| |
| try |
| { |
| |
| fPrivate->fMeta->SerializeToBuffer (&s, |
| formatOption | |
| (asPacket ? 0 |
| : kXMP_OmitPacketWrapper), |
| (asPacket ? padBytes |
| : 0), |
| "", |
| " "); |
| |
| } |
| |
| CATCH_XMP ("SerializeToBuffer", true) |
| |
| } |
| |
| uint32 packetLen = (uint32) s.size (); |
| |
| if (forJPEG && asPacket && padBytes > 0 && targetBytes <= kJPEG_XMP_Limit && |
| packetLen > kJPEG_XMP_Limit) |
| { |
| |
| uint32 overLimitCount = packetLen - kJPEG_XMP_Limit; |
| |
| if (overLimitCount > padBytes) |
| { |
| padBytes = 0; |
| } |
| else |
| { |
| padBytes -= overLimitCount; |
| } |
| |
| try |
| { |
| |
| fPrivate->fMeta->SerializeToBuffer (&s, |
| formatOption, |
| padBytes, |
| "", |
| " "); |
| |
| } |
| |
| CATCH_XMP ("SerializeToBuffer", true) |
| |
| packetLen = (uint32) s.size (); |
| |
| } |
| |
| if (packetLen) |
| { |
| |
| AutoPtr<dng_memory_block> buffer (allocator.Allocate (packetLen)); |
| |
| memcpy (buffer->Buffer (), s.c_str (), packetLen); |
| |
| return buffer.Release (); |
| |
| } |
| |
| } |
| |
| return NULL; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_xmp_sdk::PackageForJPEG (dng_memory_allocator &allocator, |
| AutoPtr<dng_memory_block> &stdBlock, |
| AutoPtr<dng_memory_block> &extBlock, |
| dng_string &extDigest) const |
| { |
| |
| if (HasMeta ()) |
| { |
| |
| TXMP_STRING_TYPE stdStr; |
| TXMP_STRING_TYPE extStr; |
| TXMP_STRING_TYPE digestStr; |
| |
| try |
| { |
| |
| SXMPUtils::PackageForJPEG (*fPrivate->fMeta, |
| &stdStr, |
| &extStr, |
| &digestStr); |
| |
| } |
| |
| CATCH_XMP ("PackageForJPEG", true) |
| |
| uint32 stdLen = (uint32) stdStr.size (); |
| uint32 extLen = (uint32) extStr.size (); |
| |
| if (stdLen) |
| { |
| |
| stdBlock.Reset (allocator.Allocate (stdLen)); |
| |
| memcpy (stdBlock->Buffer (), stdStr.c_str (), stdLen); |
| |
| } |
| |
| if (extLen) |
| { |
| |
| extBlock.Reset (allocator.Allocate (extLen)); |
| |
| memcpy (extBlock->Buffer (), extStr.c_str (), extLen); |
| |
| if (digestStr.size () != 32) |
| { |
| ThrowProgramError (); |
| } |
| |
| extDigest.Set (digestStr.c_str ()); |
| |
| } |
| |
| } |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_xmp_sdk::MergeFromJPEG (const dng_xmp_sdk *xmp) |
| { |
| |
| if (xmp && xmp->HasMeta ()) |
| { |
| |
| NeedMeta (); |
| |
| try |
| { |
| |
| SXMPUtils::MergeFromJPEG (fPrivate->fMeta, |
| *xmp->fPrivate->fMeta); |
| |
| } |
| |
| CATCH_XMP ("MergeFromJPEG", true) |
| |
| } |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_xmp_sdk::ReplaceXMP (dng_xmp_sdk *xmp) |
| { |
| |
| ClearMeta (); |
| |
| if (xmp && xmp->HasMeta ()) |
| { |
| |
| fPrivate->fMeta = xmp->fPrivate->fMeta; |
| |
| xmp->fPrivate->fMeta = NULL; |
| |
| } |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| bool dng_xmp_sdk::IteratePaths (IteratePathsCallback *callback, |
| void *callbackData, |
| const char* startingNS, |
| const char* startingPath) |
| { |
| |
| if (HasMeta ()) |
| { |
| |
| try |
| { |
| |
| SXMPIterator iter (*fPrivate->fMeta, startingNS, startingPath); |
| |
| TXMP_STRING_TYPE ns; |
| TXMP_STRING_TYPE prop; |
| |
| while (iter.Next (&ns, |
| &prop, |
| NULL, |
| NULL)) |
| { |
| |
| if (!callback (ns .c_str (), |
| prop.c_str (), |
| callbackData)) |
| { |
| |
| return false; |
| |
| } |
| |
| } |
| |
| } |
| |
| CATCH_XMP ("IteratePaths", true) |
| |
| } |
| |
| return true; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| #if qDNGXMPDocOps |
| |
| /*****************************************************************************/ |
| |
| void dng_xmp_sdk::DocOpsOpenXMP (const char *srcMIMI) |
| { |
| |
| if (srcMIMI [0]) |
| { |
| |
| NeedMeta (); |
| |
| try |
| { |
| |
| SXMPDocOps docOps; |
| |
| docOps.OpenXMP (fPrivate->fMeta, |
| srcMIMI); |
| |
| } |
| |
| CATCH_XMP ("DocOpsOpenXMP", false) |
| |
| Set (XMP_NS_DC, |
| "format", |
| srcMIMI); |
| |
| } |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_xmp_sdk::DocOpsPrepareForSave (const char *srcMIMI, |
| const char *dstMIMI, |
| bool newPath) |
| { |
| |
| NeedMeta (); |
| |
| try |
| { |
| |
| SXMPDocOps docOps; |
| |
| docOps.OpenXMP (fPrivate->fMeta, |
| srcMIMI, |
| "old path"); |
| |
| docOps.NoteChange (kXMP_Part_All); |
| |
| docOps.PrepareForSave (dstMIMI, |
| newPath ? "new path" : "old path"); |
| |
| } |
| |
| CATCH_XMP ("DocOpsPrepareForSave", false) |
| |
| Set (XMP_NS_DC, |
| "format", |
| dstMIMI); |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_xmp_sdk::DocOpsUpdateMetadata (const char *srcMIMI) |
| { |
| |
| NeedMeta (); |
| |
| try |
| { |
| |
| SXMPDocOps docOps; |
| |
| docOps.OpenXMP (fPrivate->fMeta, |
| srcMIMI); |
| |
| docOps.NoteChange (kXMP_Part_Metadata); |
| |
| docOps.PrepareForSave (srcMIMI); |
| |
| } |
| |
| CATCH_XMP ("DocOpsUpdateMetadata", false) |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| #endif |
| |
| /*****************************************************************************/ |