• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*****************************************************************************/
2 // Copyright 2006-2012 Adobe Systems Incorporated
3 // All Rights Reserved.
4 //
5 // NOTICE:  Adobe permits you to use, modify, and distribute this file in
6 // accordance with the terms of the Adobe license agreement accompanying it.
7 /*****************************************************************************/
8 
9 /* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_xmp_sdk.cpp#4 $ */
10 /* $DateTime: 2012/09/05 12:31:51 $ */
11 /* $Change: 847652 $ */
12 /* $Author: tknoll $ */
13 
14 /*****************************************************************************/
15 
16 #include "dng_xmp_sdk.h"
17 
18 #include "dng_auto_ptr.h"
19 #include "dng_assertions.h"
20 #include "dng_exceptions.h"
21 #include "dng_flags.h"
22 #include "dng_host.h"
23 #include "dng_memory.h"
24 #include "dng_string.h"
25 #include "dng_string_list.h"
26 #include "dng_utils.h"
27 
28 /*****************************************************************************/
29 
30 #if qMacOS
31 #ifndef MAC_ENV
32 #define MAC_ENV 1
33 #endif
34 #endif
35 
36 #if qWinOS
37 #ifndef WIN_ENV
38 #define WIN_ENV 1
39 #endif
40 #endif
41 
42 #include <new>
43 #include <string>
44 
45 #define TXMP_STRING_TYPE std::string
46 
47 #define XMP_INCLUDE_XMPFILES qDNGXMPFiles
48 
49 #define XMP_StaticBuild 1
50 
51 #include "XMP.incl_cpp"
52 
53 /*****************************************************************************/
54 
55 const char *XMP_NS_TIFF	      = "http://ns.adobe.com/tiff/1.0/";
56 const char *XMP_NS_EXIF	      = "http://ns.adobe.com/exif/1.0/";
57 const char *XMP_NS_PHOTOSHOP  = "http://ns.adobe.com/photoshop/1.0/";
58 const char *XMP_NS_XAP        = "http://ns.adobe.com/xap/1.0/";
59 const char *XMP_NS_XAP_RIGHTS = "http://ns.adobe.com/xap/1.0/rights/";
60 const char *XMP_NS_DC		  = "http://purl.org/dc/elements/1.1/";
61 const char *XMP_NS_XMP_NOTE   = "http://ns.adobe.com/xmp/note/";
62 const char *XMP_NS_MM         = "http://ns.adobe.com/xap/1.0/mm/";
63 
64 const char *XMP_NS_CRS		  = "http://ns.adobe.com/camera-raw-settings/1.0/";
65 const char *XMP_NS_CRSS		  = "http://ns.adobe.com/camera-raw-saved-settings/1.0/";
66 const char *XMP_NS_AUX		  = "http://ns.adobe.com/exif/1.0/aux/";
67 
68 const char *XMP_NS_LCP		  = "http://ns.adobe.com/photoshop/1.0/camera-profile";
69 
70 const char *XMP_NS_IPTC		  = "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/";
71 const char *XMP_NS_IPTC_EXT   = "http://iptc.org/std/Iptc4xmpExt/2008-02-29/";
72 
73 const char *XMP_NS_CRX 		  = "http://ns.adobe.com/lightroom-settings-experimental/1.0/";
74 
75 const char *XMP_NS_DNG		  = "http://ns.adobe.com/dng/1.0/";
76 
77 /******************************************************************************/
78 
79 #define CATCH_XMP(routine, fatal)\
80 	\
81 	catch (std::bad_alloc &)\
82 		{\
83 		DNG_REPORT ("Info: XMP " routine " threw memory exception");\
84 		ThrowMemoryFull ();\
85 		}\
86 	\
87 	catch (XMP_Error &error)\
88 		{\
89 		const char *errMessage = error.GetErrMsg ();\
90 		if (errMessage && strlen (errMessage) <= 128)\
91 			{\
92 			char errBuffer [256];\
93 			sprintf (errBuffer, "Info: XMP " routine " threw '%s' exception", errMessage);\
94 			DNG_REPORT ( errBuffer);\
95 			}\
96 		else\
97 			{\
98 			DNG_REPORT ("Info: XMP " routine " threw unnamed exception");\
99 			}\
100 		if (fatal) ThrowProgramError ();\
101 		}\
102 	\
103 	catch (...)\
104 		{\
105 		DNG_REPORT ("Info: XMP " routine " threw unknown exception");\
106 		if (fatal) ThrowProgramError ();\
107 		}
108 
109 /*****************************************************************************/
110 
111 class dng_xmp_private
112 	{
113 
114 	public:
115 
116 		SXMPMeta *fMeta;
117 
dng_xmp_private()118 		dng_xmp_private ()
119 			:	fMeta (NULL)
120 			{
121 			}
122 
123 		dng_xmp_private (const dng_xmp_private &xmp);
124 
~dng_xmp_private()125 		~dng_xmp_private ()
126 			{
127 			if (fMeta)
128 				{
129 				delete fMeta;
130 				}
131 			}
132 
133 	private:
134 
135 		// Hidden assignment operator.
136 
137 		dng_xmp_private & operator= (const dng_xmp_private &xmp);
138 
139 	};
140 
141 /*****************************************************************************/
142 
dng_xmp_private(const dng_xmp_private & xmp)143 dng_xmp_private::dng_xmp_private (const dng_xmp_private &xmp)
144 
145 	:	fMeta (NULL)
146 
147 	{
148 
149 	if (xmp.fMeta)
150 		{
151 
152 		fMeta = new SXMPMeta (xmp.fMeta->Clone (0));
153 
154 		if (!fMeta)
155 			{
156 			ThrowMemoryFull ();
157 			}
158 
159 		}
160 
161 	}
162 
163 /*****************************************************************************/
164 
dng_xmp_sdk()165 dng_xmp_sdk::dng_xmp_sdk ()
166 
167 	:	fPrivate (NULL)
168 
169 	{
170 
171 	fPrivate = new dng_xmp_private;
172 
173 	if (!fPrivate)
174 		{
175 		ThrowMemoryFull ();
176 		}
177 
178 	}
179 
180 /*****************************************************************************/
181 
dng_xmp_sdk(const dng_xmp_sdk & sdk)182 dng_xmp_sdk::dng_xmp_sdk (const dng_xmp_sdk &sdk)
183 
184 	:	fPrivate (NULL)
185 
186 	{
187 
188 	fPrivate = new dng_xmp_private (*sdk.fPrivate);
189 
190 	if (!fPrivate)
191 		{
192 		ThrowMemoryFull ();
193 		}
194 
195 	}
196 
197 /*****************************************************************************/
198 
~dng_xmp_sdk()199 dng_xmp_sdk::~dng_xmp_sdk ()
200 	{
201 
202 	if (fPrivate)
203 		{
204 		delete fPrivate;
205 		}
206 
207 	}
208 
209 /*****************************************************************************/
210 
211 static bool gInitializedXMP = false;
212 
213 /*****************************************************************************/
214 
InitializeSDK(dng_xmp_namespace * extraNamespaces,const char * software)215 void dng_xmp_sdk::InitializeSDK (dng_xmp_namespace * extraNamespaces,
216 								 const char *software)
217 	{
218 
219 	if (!gInitializedXMP)
220 		{
221 
222 		try
223 			{
224 
225 			if (!SXMPMeta::Initialize ())
226 				{
227 				ThrowProgramError ();
228 				}
229 
230 			// Register Lightroom beta settings namespace.
231 			// We no longer read this but I don't want to cut it out this close
232 			// to a release. [bruzenak]
233 
234 				{
235 
236 				TXMP_STRING_TYPE ss;
237 
238 				SXMPMeta::RegisterNamespace (XMP_NS_CRX,
239 											 "crx",
240 											 &ss);
241 
242 				}
243 
244 			// Register CRSS snapshots namespace
245 
246 				{
247 
248 				TXMP_STRING_TYPE ss;
249 
250 				SXMPMeta::RegisterNamespace (XMP_NS_CRSS,
251 											 "crss",
252 											 &ss);
253 
254 				}
255 
256 			// Register LCP (lens correction profiles) namespace
257 
258 				{
259 
260 				TXMP_STRING_TYPE ss;
261 
262 				SXMPMeta::RegisterNamespace (XMP_NS_LCP,
263 											 "stCamera",
264 											 &ss);
265 
266 				}
267 
268 			// Register DNG format metadata namespace
269 
270 				{
271 
272 				TXMP_STRING_TYPE ss;
273 
274 				SXMPMeta::RegisterNamespace (XMP_NS_DNG,
275 											 "dng",
276 											 &ss);
277 
278 				}
279 
280 			// Register extra namespaces.
281 
282 			if (extraNamespaces != NULL)
283 				{
284 
285 				for (; extraNamespaces->fullName != NULL; ++extraNamespaces)
286 					{
287 
288 					TXMP_STRING_TYPE ss;
289 
290 					SXMPMeta::RegisterNamespace (extraNamespaces->fullName,
291 												 extraNamespaces->shortName,
292 												 &ss);
293 
294 					}
295 
296 				}
297 
298 			#if qDNGXMPFiles
299 
300 			#if qLinux
301             if (!SXMPFiles::Initialize (kXMPFiles_IgnoreLocalText))
302         	#else
303             if (!SXMPFiles::Initialize ())
304        	 	#endif
305 				{
306 				ThrowProgramError ();
307 				}
308 
309 			#endif
310 
311 			#if qDNGXMPDocOps
312 
313 			if (software)
314 				{
315 
316 				SXMPDocOps::SetAppName (software);
317 
318 				}
319 
320 			#else
321 
322 			(void) software;
323 
324 			#endif
325 
326 			}
327 
328 		CATCH_XMP ("Initialization", true)
329 
330 	    gInitializedXMP = true;
331 
332 		}
333 
334 	}
335 
336 /******************************************************************************/
337 
TerminateSDK()338 void dng_xmp_sdk::TerminateSDK ()
339 	{
340 
341 	if (gInitializedXMP)
342 		{
343 
344 		try
345 			{
346 
347 			#if qDNGXMPFiles
348 
349 			SXMPFiles::Terminate ();
350 
351 			#endif
352 
353 			SXMPMeta::Terminate ();
354 
355 			}
356 
357 		catch (...)
358 			{
359 
360 			}
361 
362 		gInitializedXMP = false;
363 
364 		}
365 
366 	}
367 
368 /******************************************************************************/
369 
HasMeta() const370 bool dng_xmp_sdk::HasMeta () const
371 	{
372 
373 	if (fPrivate->fMeta)
374 		{
375 
376 		return true;
377 
378 		}
379 
380 	return false;
381 
382 	}
383 
384 /******************************************************************************/
385 
ClearMeta()386 void dng_xmp_sdk::ClearMeta ()
387 	{
388 
389 	if (HasMeta ())
390 		{
391 
392 		delete fPrivate->fMeta;
393 
394 		fPrivate->fMeta = NULL;
395 
396 		}
397 
398 	}
399 
400 /******************************************************************************/
401 
MakeMeta()402 void dng_xmp_sdk::MakeMeta ()
403 	{
404 
405 	ClearMeta ();
406 
407 	InitializeSDK ();
408 
409 	try
410 		{
411 
412 		fPrivate->fMeta = new SXMPMeta;
413 
414 		if (!fPrivate->fMeta)
415 			{
416 
417 			ThrowMemoryFull ();
418 
419 			}
420 
421 		}
422 
423 	CATCH_XMP ("MakeMeta", true)
424 
425 	}
426 
427 /******************************************************************************/
428 
NeedMeta()429 void dng_xmp_sdk::NeedMeta ()
430 	{
431 
432 	if (!HasMeta ())
433 		{
434 
435 		MakeMeta ();
436 
437 		}
438 
439 	}
440 
441 /******************************************************************************/
442 
GetPrivateMeta()443 void * dng_xmp_sdk::GetPrivateMeta ()
444 	{
445 
446 	NeedMeta ();
447 
448 	return (void *) fPrivate->fMeta;
449 
450 	}
451 
452 /******************************************************************************/
453 
Parse(dng_host & host,const char * buffer,uint32 count)454 void dng_xmp_sdk::Parse (dng_host &host,
455 						 const char *buffer,
456 						 uint32 count)
457 	{
458 
459 	MakeMeta ();
460 
461 	try
462 		{
463 
464 		try
465 			{
466 
467 			fPrivate->fMeta->ParseFromBuffer (buffer, count);
468 
469 			}
470 
471 	    CATCH_XMP ("ParseFromBuffer", true)
472 
473 	    }
474 
475 	catch (dng_exception &except)
476 		{
477 
478 		ClearMeta ();
479 
480 		if (host.IsTransientError (except.ErrorCode ()))
481 			{
482 
483 			throw;
484 
485 			}
486 
487 		ThrowBadFormat ();
488 
489 		}
490 
491 	}
492 
493 /*****************************************************************************/
494 
AppendArrayItem(const char * ns,const char * arrayName,const char * itemValue,bool isBag,bool propIsStruct)495 void dng_xmp_sdk::AppendArrayItem (const char *ns,
496 								   const char *arrayName,
497 								   const char *itemValue,
498 								   bool isBag,
499 								   bool propIsStruct)
500 	{
501 
502 	NeedMeta();
503 
504 	try
505 		{
506 
507 		fPrivate->fMeta->AppendArrayItem (ns,
508 										  arrayName,
509 										  isBag ? kXMP_PropValueIsArray
510 												: kXMP_PropArrayIsOrdered,
511 										  itemValue,
512 										  propIsStruct ? kXMP_PropValueIsStruct
513 													   : 0);
514 
515 		}
516 	CATCH_XMP ("AppendArrayItem", true )
517 
518 	}
519 
520 /*****************************************************************************/
521 
CountArrayItems(const char * ns,const char * path) const522 int32 dng_xmp_sdk::CountArrayItems (const char *ns,
523 								    const char *path) const
524 	{
525 
526 	if (HasMeta ())
527 		{
528 
529 		try
530 			{
531 
532 			return fPrivate->fMeta->CountArrayItems (ns, path);
533 
534 			}
535 
536 		CATCH_XMP ("CountArrayItems", false)
537 
538 		}
539 
540 	return 0;
541 
542 	}
543 
544 /*****************************************************************************/
545 
Exists(const char * ns,const char * path) const546 bool dng_xmp_sdk::Exists (const char *ns,
547 					 	  const char *path) const
548 	{
549 
550 	if (HasMeta ())
551 		{
552 
553 		try
554 			{
555 
556 			return fPrivate->fMeta->DoesPropertyExist (ns, path);
557 
558 			}
559 
560 		catch (...)
561 			{
562 
563 			// Does not exist...
564 
565 			}
566 
567 		}
568 
569 	return false;
570 
571 	}
572 
573 /*****************************************************************************/
574 
HasNameSpace(const char * ns) const575 bool dng_xmp_sdk::HasNameSpace (const char *ns) const
576 	{
577 
578 	bool result = false;
579 
580 	if (HasMeta ())
581 		{
582 
583 		try
584 			{
585 
586 			SXMPIterator iter (*fPrivate->fMeta, ns);
587 
588 			TXMP_STRING_TYPE nsTemp;
589 			TXMP_STRING_TYPE prop;
590 
591 			if (iter.Next (&nsTemp,
592 						   &prop,
593 						   NULL,
594 						   NULL))
595 				{
596 
597 				result = true;
598 
599 				}
600 
601 			}
602 
603 		CATCH_XMP ("HasNameSpace", true)
604 
605 		}
606 
607 	return result;
608 
609 	}
610 
611 /*****************************************************************************/
612 
Remove(const char * ns,const char * path)613 void dng_xmp_sdk::Remove (const char *ns,
614 				     	  const char *path)
615 	{
616 
617 	if (HasMeta ())
618 		{
619 
620 		try
621 			{
622 
623 			fPrivate->fMeta->DeleteProperty (ns, path);
624 
625 			}
626 
627 		CATCH_XMP ("DeleteProperty", false)
628 
629 		}
630 
631 	}
632 
633 /*****************************************************************************/
634 
RemoveProperties(const char * ns)635 void dng_xmp_sdk::RemoveProperties (const char *ns)
636 	{
637 
638 	if (HasMeta ())
639 		{
640 
641 		try
642 			{
643 
644 			SXMPUtils::RemoveProperties (fPrivate->fMeta,
645 										 ns,
646 										 NULL,
647 										 kXMPUtil_DoAllProperties);
648 
649 			}
650 
651 		catch (...)
652 			{
653 
654 			}
655 
656 		}
657 
658 	}
659 
660 /*****************************************************************************/
661 
IsEmptyString(const char * ns,const char * path)662 bool dng_xmp_sdk::IsEmptyString (const char *ns,
663 								 const char *path)
664 	{
665 
666 	if (HasMeta ())
667 		{
668 
669 		try
670 			{
671 
672 			TXMP_STRING_TYPE ss;
673 
674 			XMP_OptionBits options = 0;
675 
676 			if (fPrivate->fMeta->GetProperty (ns,
677 											  path,
678 											  &ss,
679 											  &options))
680 				{
681 
682 				// Item must be simple.
683 
684 				if (XMP_PropIsSimple (options))
685 					{
686 
687 					// Check for null strings.
688 
689 					return (ss.c_str ()     == 0 ||
690 							ss.c_str () [0] == 0);
691 
692 					}
693 
694 				}
695 
696 			}
697 
698 		CATCH_XMP ("IsEmptyString", false)
699 
700 		}
701 
702 	return false;
703 
704 	}
705 
706 /*****************************************************************************/
707 
IsEmptyArray(const char * ns,const char * path)708 bool dng_xmp_sdk::IsEmptyArray (const char *ns,
709 								const char *path)
710 	{
711 
712 	if (HasMeta ())
713 		{
714 
715 		try
716 			{
717 
718 			TXMP_STRING_TYPE ss;
719 
720 			XMP_OptionBits options = 0;
721 
722 			if (fPrivate->fMeta->GetProperty (ns,
723 											  path,
724 											  &ss,
725 											  &options))
726 				{
727 
728 				if (XMP_PropIsArray (options))
729 					{
730 
731 					if (fPrivate->fMeta->GetArrayItem (ns,
732 													   path,
733 													   1,
734 													   &ss,
735 													   &options))
736 						{
737 
738 						// If the first item is a null string...
739 
740 						if (XMP_PropIsSimple (options))
741 							{
742 
743 							if ((ss.c_str ()     == 0 ||
744 								 ss.c_str () [0] == 0))
745 								{
746 
747 								// And there is no second item.
748 
749 								if (!fPrivate->fMeta->GetArrayItem (ns,
750 																	path,
751 																	2,
752 																	&ss,
753 																	&options))
754 									{
755 
756 									// Then we have an empty array.
757 
758 									return true;
759 
760 									}
761 
762 								}
763 
764 							}
765 
766 						}
767 
768 					else
769 						{
770 
771 						// Unable to get first item, so array is empty.
772 
773 						return true;
774 
775 						}
776 
777 					}
778 
779 				}
780 
781 			}
782 
783 		CATCH_XMP ("IsEmptyArray", false)
784 
785 		}
786 
787 	return false;
788 
789 	}
790 
791 /*****************************************************************************/
792 
ComposeArrayItemPath(const char * ns,const char * arrayName,int32 index,dng_string & s) const793 void dng_xmp_sdk::ComposeArrayItemPath (const char *ns,
794 										const char *arrayName,
795 										int32 index,
796 										dng_string &s) const
797 	{
798 
799 	try
800 		{
801 
802 		std::string ss;
803 
804 		SXMPUtils::ComposeArrayItemPath (ns, arrayName, index, &ss);
805 
806 		s.Set (ss.c_str ());
807 
808 		return;
809 
810 		}
811 
812 	CATCH_XMP ("ComposeArrayItemPath", true)
813 
814 	}
815 
816 /*****************************************************************************/
817 
ComposeStructFieldPath(const char * ns,const char * structName,const char * fieldNS,const char * fieldName,dng_string & s) const818 void dng_xmp_sdk::ComposeStructFieldPath (const char *ns,
819 										  const char *structName,
820 										  const char *fieldNS,
821 										  const char *fieldName,
822 										  dng_string &s) const
823 	{
824 
825 	try
826 		{
827 
828 		std::string ss;
829 
830 		SXMPUtils::ComposeStructFieldPath (ns,
831 										   structName,
832 										   fieldNS,
833 										   fieldName,
834 										   &ss);
835 
836 		s.Set (ss.c_str ());
837 
838 		return;
839 
840 		}
841 
842 	CATCH_XMP ("ComposeStructFieldPath", true)
843 
844 	}
845 
846 /*****************************************************************************/
847 
GetNamespacePrefix(const char * uri,dng_string & s) const848 bool dng_xmp_sdk::GetNamespacePrefix (const char *uri,
849 									  dng_string &s) const
850 	{
851 
852 	bool result = false;
853 
854 	if (HasMeta ())
855 		{
856 
857 		try
858 			{
859 
860 			std::string ss;
861 
862 			fPrivate->fMeta->GetNamespacePrefix (uri, &ss);
863 
864 			s.Set (ss.c_str ());
865 
866 			result = true;
867 
868 			}
869 
870 		CATCH_XMP ("GetNamespacePrefix", false)
871 
872 		}
873 
874 	return result;
875 
876 	}
877 
878 /*****************************************************************************/
879 
GetString(const char * ns,const char * path,dng_string & s) const880 bool dng_xmp_sdk::GetString (const char *ns,
881 				   		     const char *path,
882 				   		     dng_string &s) const
883 	{
884 
885 	bool result = false;
886 
887 	if (HasMeta ())
888 		{
889 
890 		try
891 			{
892 
893 			TXMP_STRING_TYPE ss;
894 
895 			if (fPrivate->fMeta->GetProperty (ns, path, &ss, NULL))
896 				{
897 
898 				s.Set (ss.c_str ());
899 
900 				result = true;
901 
902 				}
903 
904 			}
905 
906 		CATCH_XMP ("GetProperty", false)
907 
908 		}
909 
910 	return result;
911 
912 	}
913 
914 /*****************************************************************************/
915 
ValidateStringList(const char * ns,const char * path)916 void dng_xmp_sdk::ValidateStringList (const char *ns,
917 								      const char *path)
918 	{
919 
920 	if (Exists (ns, path))
921 		{
922 
923 		bool bogus = true;
924 
925 		try
926 			{
927 
928 			XMP_Index index = 1;
929 
930 			TXMP_STRING_TYPE ss;
931 
932 			while (fPrivate->fMeta->GetArrayItem (ns,
933 												  path,
934 												  index++,
935 												  &ss,
936 												  NULL))
937 				{
938 
939 				}
940 
941 			bogus = false;
942 
943 			}
944 
945 		CATCH_XMP ("GetArrayItem", false)
946 
947 		if (bogus)
948 			{
949 
950 			Remove (ns, path);
951 
952 			}
953 
954 		}
955 
956 	}
957 
958 /*****************************************************************************/
959 
GetStringList(const char * ns,const char * path,dng_string_list & list) const960 bool dng_xmp_sdk::GetStringList (const char *ns,
961 								 const char *path,
962 								 dng_string_list &list) const
963 	{
964 
965 	bool result = false;
966 
967 	if (HasMeta ())
968 		{
969 
970 		try
971 			{
972 
973 			XMP_Index index = 1;
974 
975 			TXMP_STRING_TYPE ss;
976 
977 			while (fPrivate->fMeta->GetArrayItem (ns,
978 												  path,
979 												  index++,
980 												  &ss,
981 												  NULL))
982 				{
983 
984 				dng_string s;
985 
986 				s.Set (ss.c_str ());
987 
988 				list.Append (s);
989 
990 				result = true;
991 
992 				}
993 
994 			}
995 
996 		CATCH_XMP ("GetArrayItem", false)
997 
998 		}
999 
1000 	return result;
1001 
1002 	}
1003 
1004 /*****************************************************************************/
1005 
GetAltLangDefault(const char * ns,const char * path,dng_string & s) const1006 bool dng_xmp_sdk::GetAltLangDefault (const char *ns,
1007 									 const char *path,
1008 									 dng_string &s) const
1009 	{
1010 
1011 	bool result = false;
1012 
1013 	if (HasMeta ())
1014 		{
1015 
1016 		try
1017 			{
1018 
1019 			TXMP_STRING_TYPE ss;
1020 
1021 			if (fPrivate->fMeta->GetLocalizedText (ns,
1022 												   path,
1023 												   "x-default",
1024 									  	   		   "x-default",
1025 												   NULL,
1026 									  	  		   &ss,
1027 												   NULL))
1028 				{
1029 
1030 				s.Set (ss.c_str ());
1031 
1032 				result = true;
1033 
1034 				}
1035 			//
1036 			// Special Case: treat the following two representation equivalently.
1037 			// The first is an empty alt lang array; the second is an array with
1038 			// an empty item. It seems that xmp lib could be generating both under
1039 			// some circumstances!
1040 			//
1041 			// <dc:description>
1042 			//	<rdf:Alt/>
1043 			// </dc:description>
1044 			//
1045 			// and
1046 			//
1047 			// <dc:description>
1048 			//  <rdf:Alt>
1049 			//   <rdf:li xml:lang="x-default"/>
1050 			//  </rdf:Alt>
1051 			// </dc:description>
1052 			//
1053 			else if (fPrivate->fMeta->GetProperty (ns,
1054 												   path,
1055 												   &ss,
1056 												   NULL))
1057 				{
1058 
1059 				if (ss.empty ())
1060 					{
1061 
1062 					s.Clear ();
1063 
1064 					result = true;
1065 
1066 					}
1067 
1068 				}
1069 
1070 			}
1071 
1072 		CATCH_XMP ("GetLocalizedText", false)
1073 
1074 		}
1075 
1076 	return result;
1077 
1078 	}
1079 
1080 /*****************************************************************************/
1081 
GetStructField(const char * ns,const char * path,const char * fieldNS,const char * fieldName,dng_string & s) const1082 bool dng_xmp_sdk::GetStructField (const char *ns,
1083 								  const char *path,
1084 								  const char *fieldNS,
1085 								  const char *fieldName,
1086 								  dng_string &s) const
1087 	{
1088 
1089 	bool result = false;
1090 
1091 	if (HasMeta ())
1092 		{
1093 
1094 		try
1095 			{
1096 
1097 			TXMP_STRING_TYPE ss;
1098 
1099 			if (fPrivate->fMeta->GetStructField (ns,
1100 												 path,
1101 												 fieldNS,
1102 												 fieldName,
1103 												 &ss,
1104 												 NULL))
1105 				{
1106 
1107 				s.Set (ss.c_str ());
1108 
1109 				result = true;
1110 
1111 				}
1112 
1113 			}
1114 
1115 		CATCH_XMP ("GetStructField", false)
1116 
1117 		}
1118 
1119 	return result;
1120 
1121 	}
1122 
1123 /*****************************************************************************/
1124 
Set(const char * ns,const char * path,const char * text)1125 void dng_xmp_sdk::Set (const char *ns,
1126 				  	   const char *path,
1127 				  	   const char *text)
1128 	{
1129 
1130 	NeedMeta ();
1131 
1132 	try
1133 		{
1134 
1135 		fPrivate->fMeta->SetProperty (ns, path, text);
1136 
1137 		return;
1138 
1139 		}
1140 
1141 	catch (...)
1142 		{
1143 
1144 		// Failed for some reason.
1145 
1146 		}
1147 
1148 	// Remove existing value and try again.
1149 
1150 	Remove (ns, path);
1151 
1152 	try
1153 		{
1154 
1155 		fPrivate->fMeta->SetProperty (ns, path, text);
1156 
1157 		}
1158 
1159 	CATCH_XMP ("SetProperty", true)
1160 
1161 	}
1162 
1163 /*****************************************************************************/
1164 
SetString(const char * ns,const char * path,const dng_string & s)1165 void dng_xmp_sdk::SetString (const char *ns,
1166 				  			 const char *path,
1167 				  			 const dng_string &s)
1168 	{
1169 
1170 	dng_string ss (s);
1171 
1172 	ss.SetLineEndings ('\n');
1173 
1174 	ss.StripLowASCII ();
1175 
1176 	Set (ns, path, ss.Get ());
1177 
1178 	}
1179 
1180 /*****************************************************************************/
1181 
SetStringList(const char * ns,const char * path,const dng_string_list & list,bool isBag)1182 void dng_xmp_sdk::SetStringList (const char *ns,
1183 				  		    	 const char *path,
1184 				  		    	 const dng_string_list &list,
1185 				  		    	 bool isBag)
1186 	{
1187 
1188 	// Remove any existing structure.
1189 
1190 	Remove (ns, path);
1191 
1192 	// If list is not empty, add the items.
1193 
1194 	if (list.Count ())
1195 		{
1196 
1197 		NeedMeta ();
1198 
1199 		for (uint32 index = 0; index < list.Count (); index++)
1200 			{
1201 
1202 			dng_string s (list [index]);
1203 
1204 			s.SetLineEndings ('\n');
1205 
1206 			s.StripLowASCII ();
1207 
1208 			try
1209 				{
1210 
1211 				fPrivate->fMeta->AppendArrayItem (ns,
1212 												  path,
1213 												  isBag ? kXMP_PropValueIsArray
1214 														: kXMP_PropArrayIsOrdered,
1215 												  s.Get ());
1216 
1217 				}
1218 
1219 			CATCH_XMP ("AppendArrayItem", true)
1220 
1221 			}
1222 
1223 		}
1224 
1225 	}
1226 
1227 /*****************************************************************************/
1228 
SetAltLangDefault(const char * ns,const char * path,const dng_string & s)1229 void dng_xmp_sdk::SetAltLangDefault (const char *ns,
1230 									 const char *path,
1231 									 const dng_string &s)
1232 	{
1233 
1234 	NeedMeta ();
1235 
1236 	Remove (ns, path);
1237 
1238 	dng_string ss (s);
1239 
1240 	ss.SetLineEndings ('\n');
1241 
1242 	ss.StripLowASCII ();
1243 
1244 	try
1245 		{
1246 
1247 		fPrivate->fMeta->SetLocalizedText (ns,
1248 									  	   path,
1249 									  	   "x-default",
1250 									  	   "x-default",
1251 									  	   ss.Get ());
1252 
1253 		}
1254 
1255 	CATCH_XMP ("SetLocalizedText", true)
1256 
1257 	}
1258 
1259 /*****************************************************************************/
1260 
SetStructField(const char * ns,const char * path,const char * fieldNS,const char * fieldName,const char * text)1261 void dng_xmp_sdk::SetStructField (const char *ns,
1262 								  const char *path,
1263 								  const char *fieldNS,
1264 								  const char *fieldName,
1265 								  const char *text)
1266 	{
1267 
1268 	NeedMeta ();
1269 
1270 	try
1271 		{
1272 
1273 		fPrivate->fMeta->SetStructField (ns,
1274 							  			 path,
1275 							  			 fieldNS,
1276 							  			 fieldName,
1277 							  			 text);
1278 
1279 		}
1280 
1281 	CATCH_XMP ("SetStructField", true)
1282 
1283 	}
1284 
1285 /*****************************************************************************/
1286 
DeleteStructField(const char * ns,const char * structName,const char * fieldNS,const char * fieldName)1287 void dng_xmp_sdk::DeleteStructField (const char *ns,
1288 									 const char *structName,
1289 									 const char *fieldNS,
1290 						             const char *fieldName)
1291 	{
1292 
1293 	if (HasMeta ())
1294 		{
1295 
1296 		try
1297 			{
1298 
1299 			fPrivate->fMeta->DeleteStructField (ns, structName, fieldNS, fieldName);
1300 
1301 			}
1302 
1303 		catch (...)
1304 			{
1305 
1306 			}
1307 
1308 		}
1309 
1310 	}
1311 
1312 /*****************************************************************************/
1313 
Serialize(dng_memory_allocator & allocator,bool asPacket,uint32 targetBytes,uint32 padBytes,bool forJPEG,bool compact) const1314 dng_memory_block * dng_xmp_sdk::Serialize (dng_memory_allocator &allocator,
1315 									       bool asPacket,
1316 									       uint32 targetBytes,
1317 									       uint32 padBytes,
1318 									       bool forJPEG,
1319 										   bool compact) const
1320 	{
1321 
1322 	// The largest XMP packet you can embed in JPEG using normal methods:
1323 
1324 	const uint32 kJPEG_XMP_Limit = 65504;
1325 
1326 	if (HasMeta ())
1327 		{
1328 
1329 		TXMP_STRING_TYPE s;
1330 
1331 		bool havePacket = false;
1332 
1333 		// Note that the XMP lib is changing its default to compact format
1334 		// in the future, so the following line will need to change.
1335 
1336 		uint32 formatOption = compact ? kXMP_UseCompactFormat : 0;
1337 
1338 	    if (asPacket && targetBytes)
1339 	    	{
1340 
1341 	    	try
1342 	    		{
1343 
1344 	    		fPrivate->fMeta->SerializeToBuffer (&s,
1345 	    											formatOption | kXMP_ExactPacketLength,
1346 	    											targetBytes,
1347 	    											"",
1348 													" ");
1349 
1350 				havePacket = true;
1351 
1352 	    		}
1353 
1354 	    	catch (...)
1355 	    		{
1356 
1357 	    		// Most likely the packet cannot fit in the target
1358 	    		// byte count.  So try again without the limit.
1359 
1360 	    		}
1361 
1362 	    	}
1363 
1364 		if (!havePacket)
1365 			{
1366 
1367 			try
1368 				{
1369 
1370 				fPrivate->fMeta->SerializeToBuffer (&s,
1371 													formatOption |
1372 													(asPacket ? 0
1373 															  : kXMP_OmitPacketWrapper),
1374 													(asPacket ? padBytes
1375 															  : 0),
1376 													"",
1377 													" ");
1378 
1379 				}
1380 
1381 			CATCH_XMP ("SerializeToBuffer", true)
1382 
1383 			}
1384 
1385 		uint32 packetLen = (uint32) s.size ();
1386 
1387 		if (forJPEG && asPacket && padBytes > 0 && targetBytes <= kJPEG_XMP_Limit &&
1388 												   packetLen   >  kJPEG_XMP_Limit)
1389 			{
1390 
1391 			uint32 overLimitCount = packetLen - kJPEG_XMP_Limit;
1392 
1393 			if (overLimitCount > padBytes)
1394 				{
1395 				padBytes = 0;
1396 				}
1397 			else
1398 				{
1399 				padBytes -= overLimitCount;
1400 				}
1401 
1402 			try
1403 				{
1404 
1405 				fPrivate->fMeta->SerializeToBuffer (&s,
1406 													formatOption,
1407 													padBytes,
1408 													"",
1409 													" ");
1410 
1411 				}
1412 
1413 			CATCH_XMP ("SerializeToBuffer", true)
1414 
1415 			packetLen = (uint32) s.size ();
1416 
1417 			}
1418 
1419 		if (packetLen)
1420 			{
1421 
1422 			AutoPtr<dng_memory_block> buffer (allocator.Allocate (packetLen));
1423 
1424 			memcpy (buffer->Buffer (), s.c_str (), packetLen);
1425 
1426 			return buffer.Release ();
1427 
1428 			}
1429 
1430 		}
1431 
1432 	return NULL;
1433 
1434 	}
1435 
1436 /*****************************************************************************/
1437 
PackageForJPEG(dng_memory_allocator & allocator,AutoPtr<dng_memory_block> & stdBlock,AutoPtr<dng_memory_block> & extBlock,dng_string & extDigest) const1438 void dng_xmp_sdk::PackageForJPEG (dng_memory_allocator &allocator,
1439 								  AutoPtr<dng_memory_block> &stdBlock,
1440 								  AutoPtr<dng_memory_block> &extBlock,
1441 								  dng_string &extDigest) const
1442 	{
1443 
1444 	if (HasMeta ())
1445 		{
1446 
1447 		TXMP_STRING_TYPE stdStr;
1448 		TXMP_STRING_TYPE extStr;
1449 		TXMP_STRING_TYPE digestStr;
1450 
1451 		try
1452 			{
1453 
1454 			SXMPUtils::PackageForJPEG (*fPrivate->fMeta,
1455 									   &stdStr,
1456 									   &extStr,
1457 									   &digestStr);
1458 
1459 			}
1460 
1461 		CATCH_XMP ("PackageForJPEG", true)
1462 
1463 		uint32 stdLen = (uint32) stdStr.size ();
1464 		uint32 extLen = (uint32) extStr.size ();
1465 
1466 		if (stdLen)
1467 			{
1468 
1469 			stdBlock.Reset (allocator.Allocate (stdLen));
1470 
1471 			memcpy (stdBlock->Buffer (), stdStr.c_str (), stdLen);
1472 
1473 			}
1474 
1475 		if (extLen)
1476 			{
1477 
1478 			extBlock.Reset (allocator.Allocate (extLen));
1479 
1480 			memcpy (extBlock->Buffer (), extStr.c_str (), extLen);
1481 
1482 			if (digestStr.size () != 32)
1483 				{
1484 				ThrowProgramError ();
1485 				}
1486 
1487 			extDigest.Set (digestStr.c_str ());
1488 
1489 			}
1490 
1491 		}
1492 
1493 	}
1494 
1495 /*****************************************************************************/
1496 
MergeFromJPEG(const dng_xmp_sdk * xmp)1497 void dng_xmp_sdk::MergeFromJPEG (const dng_xmp_sdk *xmp)
1498 	{
1499 
1500 	if (xmp && xmp->HasMeta ())
1501 		{
1502 
1503 		NeedMeta ();
1504 
1505 		try
1506 			{
1507 
1508 			SXMPUtils::MergeFromJPEG (fPrivate->fMeta,
1509 									  *xmp->fPrivate->fMeta);
1510 
1511 			}
1512 
1513 		CATCH_XMP ("MergeFromJPEG", true)
1514 
1515 		}
1516 
1517 	}
1518 
1519 /*****************************************************************************/
1520 
ReplaceXMP(dng_xmp_sdk * xmp)1521 void dng_xmp_sdk::ReplaceXMP (dng_xmp_sdk *xmp)
1522 	{
1523 
1524 	ClearMeta ();
1525 
1526 	if (xmp && xmp->HasMeta ())
1527 		{
1528 
1529 		fPrivate->fMeta = xmp->fPrivate->fMeta;
1530 
1531 		xmp->fPrivate->fMeta = NULL;
1532 
1533 		}
1534 
1535 	}
1536 
1537 /*****************************************************************************/
1538 
IteratePaths(IteratePathsCallback * callback,void * callbackData,const char * startingNS,const char * startingPath)1539 bool dng_xmp_sdk::IteratePaths (IteratePathsCallback *callback,
1540 						        void *callbackData,
1541 								const char* startingNS,
1542 								const char* startingPath)
1543 	{
1544 
1545 	if (HasMeta ())
1546 		{
1547 
1548 		try
1549 			{
1550 
1551 			SXMPIterator iter (*fPrivate->fMeta, startingNS, startingPath);
1552 
1553 			TXMP_STRING_TYPE ns;
1554 			TXMP_STRING_TYPE prop;
1555 
1556 			while (iter.Next (&ns,
1557 							  &prop,
1558 							  NULL,
1559 							  NULL))
1560 				{
1561 
1562 				if (!callback (ns  .c_str (),
1563 						  	   prop.c_str (),
1564 						  	   callbackData))
1565 					{
1566 
1567 					return false;
1568 
1569 					}
1570 
1571 				}
1572 
1573 			}
1574 
1575 		CATCH_XMP ("IteratePaths", true)
1576 
1577 		}
1578 
1579 	return true;
1580 
1581 	}
1582 
1583 /*****************************************************************************/
1584 
1585 #if qDNGXMPDocOps
1586 
1587 /*****************************************************************************/
1588 
DocOpsOpenXMP(const char * srcMIMI)1589 void dng_xmp_sdk::DocOpsOpenXMP (const char *srcMIMI)
1590 	{
1591 
1592 	if (srcMIMI [0])
1593 		{
1594 
1595 		NeedMeta ();
1596 
1597 		try
1598 			{
1599 
1600 			SXMPDocOps docOps;
1601 
1602 			docOps.OpenXMP (fPrivate->fMeta,
1603 							srcMIMI);
1604 
1605 			}
1606 
1607 		CATCH_XMP ("DocOpsOpenXMP", false)
1608 
1609 		Set (XMP_NS_DC,
1610 			 "format",
1611 			 srcMIMI);
1612 
1613 		}
1614 
1615 	}
1616 
1617 /*****************************************************************************/
1618 
DocOpsPrepareForSave(const char * srcMIMI,const char * dstMIMI,bool newPath)1619 void dng_xmp_sdk::DocOpsPrepareForSave (const char *srcMIMI,
1620 										const char *dstMIMI,
1621 										bool newPath)
1622 	{
1623 
1624 	NeedMeta ();
1625 
1626 	try
1627 		{
1628 
1629 		SXMPDocOps docOps;
1630 
1631 		docOps.OpenXMP (fPrivate->fMeta,
1632 						srcMIMI,
1633 						"old path");
1634 
1635 		docOps.NoteChange (kXMP_Part_All);
1636 
1637 		docOps.PrepareForSave (dstMIMI,
1638 							   newPath ? "new path" : "old path");
1639 
1640 		}
1641 
1642 	CATCH_XMP ("DocOpsPrepareForSave", false)
1643 
1644 	Set (XMP_NS_DC,
1645 		 "format",
1646 		 dstMIMI);
1647 
1648 	}
1649 
1650 /*****************************************************************************/
1651 
DocOpsUpdateMetadata(const char * srcMIMI)1652 void dng_xmp_sdk::DocOpsUpdateMetadata (const char *srcMIMI)
1653 	{
1654 
1655 	NeedMeta ();
1656 
1657 	try
1658 		{
1659 
1660 		SXMPDocOps docOps;
1661 
1662 		docOps.OpenXMP (fPrivate->fMeta,
1663 						srcMIMI);
1664 
1665 		docOps.NoteChange (kXMP_Part_Metadata);
1666 
1667 		docOps.PrepareForSave (srcMIMI);
1668 
1669 		}
1670 
1671 	CATCH_XMP ("DocOpsUpdateMetadata", false)
1672 
1673 	}
1674 
1675 /*****************************************************************************/
1676 
1677 #endif
1678 
1679 /*****************************************************************************/
1680