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
55const char *XMP_NS_TIFF	      = "http://ns.adobe.com/tiff/1.0/";
56const char *XMP_NS_EXIF	      = "http://ns.adobe.com/exif/1.0/";
57const char *XMP_NS_PHOTOSHOP  = "http://ns.adobe.com/photoshop/1.0/";
58const char *XMP_NS_XAP        = "http://ns.adobe.com/xap/1.0/";
59const char *XMP_NS_XAP_RIGHTS = "http://ns.adobe.com/xap/1.0/rights/";
60const char *XMP_NS_DC		  = "http://purl.org/dc/elements/1.1/";
61const char *XMP_NS_XMP_NOTE   = "http://ns.adobe.com/xmp/note/";
62const char *XMP_NS_MM         = "http://ns.adobe.com/xap/1.0/mm/";
63
64const char *XMP_NS_CRS		  = "http://ns.adobe.com/camera-raw-settings/1.0/";
65const char *XMP_NS_CRSS		  = "http://ns.adobe.com/camera-raw-saved-settings/1.0/";
66const char *XMP_NS_AUX		  = "http://ns.adobe.com/exif/1.0/aux/";
67
68const char *XMP_NS_LCP		  = "http://ns.adobe.com/photoshop/1.0/camera-profile";
69
70const char *XMP_NS_IPTC		  = "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/";
71const char *XMP_NS_IPTC_EXT   = "http://iptc.org/std/Iptc4xmpExt/2008-02-29/";
72
73const char *XMP_NS_CRX 		  = "http://ns.adobe.com/lightroom-settings-experimental/1.0/";
74
75const 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
111class dng_xmp_private
112	{
113
114	public:
115
116		SXMPMeta *fMeta;
117
118		dng_xmp_private ()
119			:	fMeta (NULL)
120			{
121			}
122
123		dng_xmp_private (const dng_xmp_private &xmp);
124
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
143dng_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
165dng_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
182dng_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
199dng_xmp_sdk::~dng_xmp_sdk ()
200	{
201
202	if (fPrivate)
203		{
204		delete fPrivate;
205		}
206
207	}
208
209/*****************************************************************************/
210
211static bool gInitializedXMP = false;
212
213/*****************************************************************************/
214
215void 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
338void 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
370bool 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
386void 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
402void 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
429void dng_xmp_sdk::NeedMeta ()
430	{
431
432	if (!HasMeta ())
433		{
434
435		MakeMeta ();
436
437		}
438
439	}
440
441/******************************************************************************/
442
443void * dng_xmp_sdk::GetPrivateMeta ()
444	{
445
446	NeedMeta ();
447
448	return (void *) fPrivate->fMeta;
449
450	}
451
452/******************************************************************************/
453
454void 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
495void 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
522int32 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
546bool 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
575bool 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
613void 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
635void 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
662bool 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
708bool 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
793void 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
818void 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
848bool 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
880bool 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
916void 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
960bool 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
1006bool 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
1082bool 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
1125void 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
1165void 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
1182void 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
1229void 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
1261void 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
1287void 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
1314dng_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
1438void 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
1497void 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
1521void 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
1539bool 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
1589void 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
1619void 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
1652void 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