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