1/*****************************************************************************/
2// Copyright 2006-2008 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.cpp#1 $ */
10/* $DateTime: 2012/05/30 13:28:51 $ */
11/* $Change: 832332 $ */
12/* $Author: tknoll $ */
13
14/*****************************************************************************/
15#if qDNGUseXMP
16
17#include "dng_xmp.h"
18
19#include "dng_assertions.h"
20#include "dng_date_time.h"
21#include "dng_exceptions.h"
22#include "dng_exif.h"
23#include "dng_image_writer.h"
24#include "dng_iptc.h"
25#include "dng_negative.h"
26#include "dng_string.h"
27#include "dng_string_list.h"
28#include "dng_utils.h"
29#include "dng_xmp_sdk.h"
30
31/*****************************************************************************/
32
33dng_xmp::dng_xmp (dng_memory_allocator &allocator)
34
35	:	fAllocator (allocator)
36
37	,	fSDK (NULL)
38
39	{
40
41	fSDK = new dng_xmp_sdk ();
42
43	if (!fSDK)
44		{
45		ThrowMemoryFull ();
46		}
47
48	}
49
50/*****************************************************************************/
51
52dng_xmp::dng_xmp (const dng_xmp &xmp)
53
54	:	fAllocator (xmp.fAllocator)
55
56	,	fSDK (NULL)
57
58	{
59
60	fSDK = new dng_xmp_sdk (*xmp.fSDK);
61
62	if (!fSDK)
63		{
64		ThrowMemoryFull ();
65		}
66
67	}
68
69/*****************************************************************************/
70
71dng_xmp::~dng_xmp ()
72	{
73
74	if (fSDK)
75		{
76
77		delete fSDK;
78
79		}
80
81	}
82
83/*****************************************************************************/
84
85dng_xmp * dng_xmp::Clone () const
86	{
87
88	dng_xmp *result = new dng_xmp (*this);
89
90	if (!result)
91		{
92		ThrowMemoryFull ();
93		}
94
95	return result;
96
97	}
98
99/*****************************************************************************/
100
101void dng_xmp::TrimDecimal (char *s)
102	{
103
104	uint32 len = (uint32) strlen (s);
105
106	while (len > 0)
107		{
108
109		if (s [len - 1] == '0')
110			s [--len] = 0;
111
112		else
113			break;
114
115		}
116
117	if (len > 0)
118		{
119
120		if (s [len - 1] == '.')
121			s [--len] = 0;
122
123		}
124
125	}
126
127/*****************************************************************************/
128
129dng_string dng_xmp::EncodeFingerprint (const dng_fingerprint &f,
130									   bool allowInvalid)
131	{
132
133	dng_string result;
134
135	if (f.IsValid () || allowInvalid)
136		{
137
138		char s [dng_fingerprint::kDNGFingerprintSize * 2 + 1];
139
140		f.ToUtf8HexString (s);
141
142		result.Set (s);
143
144		}
145
146	return result;
147
148	}
149
150/*****************************************************************************/
151
152dng_fingerprint dng_xmp::DecodeFingerprint (const dng_string &s)
153	{
154
155	dng_fingerprint result;
156
157	if (s.Length () == 32)
158		result.FromUtf8HexString (s.Get ());
159
160	return result;
161
162	}
163
164/*****************************************************************************/
165
166dng_string dng_xmp::EncodeGPSVersion (uint32 version)
167	{
168
169	dng_string result;
170
171	if (version)
172		{
173
174		uint8 b0 = (uint8) (version >> 24);
175		uint8 b1 = (uint8) (version >> 16);
176		uint8 b2 = (uint8) (version >>  8);
177		uint8 b3 = (uint8) (version      );
178
179		if (b0 <= 9 && b1 <= 9 && b2 <= 9 && b3 <= 9)
180			{
181
182   			char s [32];
183
184			sprintf (s,
185					 "%u.%u.%u.%u",
186					 (unsigned) b0,
187					 (unsigned) b1,
188					 (unsigned) b2,
189					 (unsigned) b3);
190
191			result.Set (s);
192
193			}
194
195		}
196
197	return result;
198
199	}
200
201/*****************************************************************************/
202
203uint32 dng_xmp::DecodeGPSVersion (const dng_string &s)
204	{
205
206	uint32 result = 0;
207
208	if (s.Length () == 7)
209		{
210
211		unsigned b0 = 0;
212		unsigned b1 = 0;
213		unsigned b2 = 0;
214		unsigned b3 = 0;
215
216		if (sscanf (s.Get (),
217					"%u.%u.%u.%u",
218					&b0,
219					&b1,
220					&b2,
221					&b3) == 4)
222			{
223
224			result = (b0 << 24) |
225					 (b1 << 16) |
226					 (b2 <<  8) |
227					 (b3      );
228
229			}
230
231		}
232
233	return result;
234
235	}
236
237/*****************************************************************************/
238
239dng_string dng_xmp::EncodeGPSCoordinate (const dng_string &ref,
240							    		 const dng_urational *coord)
241	{
242
243	dng_string result;
244
245	if (ref.Length () == 1 && coord [0].IsValid () &&
246							  coord [1].IsValid ())
247		{
248
249		char refChar = ForceUppercase (ref.Get () [0]);
250
251		if (refChar == 'N' ||
252			refChar == 'S' ||
253			refChar == 'E' ||
254			refChar == 'W')
255			{
256
257			char s [256];
258
259			// Use the seconds case if all three values are
260			// integers.
261
262			if (coord [0].d == 1 &&
263				coord [1].d == 1 &&
264				coord [2].d == 1)
265				{
266
267				sprintf (s,
268						 "%u,%u,%u%c",
269						 (unsigned) coord [0].n,
270						 (unsigned) coord [1].n,
271						 (unsigned) coord [2].n,
272						 refChar);
273
274				}
275
276			// Else we need to use the fractional minutes case.
277
278			else
279				{
280
281				// Find value minutes.
282
283				real64 x = coord [0].As_real64 () * 60.0 +
284						   coord [1].As_real64 () +
285						   coord [2].As_real64 () * (1.0 / 60.0);
286
287				// Round to fractional four decimal places.
288
289				uint32 y = Round_uint32 (x * 10000.0);
290
291				// Split into degrees and minutes.
292
293				uint32 d = y / (60 * 10000);
294				uint32 m = y % (60 * 10000);
295
296				char min [32];
297
298				sprintf (min, "%.4f", m * (1.0 / 10000.0));
299
300				TrimDecimal (min);
301
302				sprintf (s,
303						 "%u,%s%c",
304						 (unsigned) d,
305						 min,
306						 refChar);
307
308				}
309
310			result.Set (s);
311
312			}
313
314		}
315
316	return result;
317
318	}
319
320/*****************************************************************************/
321
322void dng_xmp::DecodeGPSCoordinate (const dng_string &s,
323								   dng_string &ref,
324								   dng_urational *coord)
325	{
326
327	ref.Clear ();
328
329	coord [0].Clear ();
330	coord [1].Clear ();
331	coord [2].Clear ();
332
333	if (s.Length () > 1)
334		{
335
336		char refChar = ForceUppercase (s.Get () [s.Length () - 1]);
337
338		if (refChar == 'N' ||
339			refChar == 'S' ||
340			refChar == 'E' ||
341			refChar == 'W')
342			{
343
344			dng_string ss (s);
345
346			ss.Truncate (ss.Length () - 1);
347
348			ss.NormalizeAsCommaSeparatedNumbers();
349
350			int degrees = 0;
351
352			real64 minutes = 0.0;
353			real64 seconds = 0.0;
354
355			int count = sscanf (ss.Get (),
356								"%d,%lf,%lf",
357								&degrees,
358								&minutes,
359								&seconds);
360
361			if (count < 1)
362				{
363				return;
364				}
365
366			// The degree, minute, second values should always be positive.
367
368			if (degrees < 0 || minutes < 0 || seconds < 0)
369				{
370				return;
371				}
372
373			coord [0] = dng_urational ((uint32) degrees, 1);
374
375			if (count <= 2)
376				{
377				coord [1].Set_real64 (minutes, 10000);
378				coord [2] = dng_urational (0, 1);
379				}
380			else
381				{
382				coord [1].Set_real64 (minutes, 1);
383				coord [2].Set_real64 (seconds, 100);
384				}
385
386			char r [2];
387
388			r [0] = refChar;
389			r [1] = 0;
390
391			ref.Set (r);
392
393			}
394
395		}
396
397	}
398
399/*****************************************************************************/
400
401dng_string dng_xmp::EncodeGPSDateTime (const dng_string &dateStamp,
402									   const dng_urational *timeStamp)
403	{
404
405	dng_string result;
406
407	if (timeStamp [0].IsValid () &&
408		timeStamp [1].IsValid () &&
409		timeStamp [2].IsValid ())
410		{
411
412 		char s [256];
413
414		char sec [32];
415
416		sprintf (sec,
417				 "%09.6f",
418				 timeStamp [2].As_real64 ());
419
420		TrimDecimal (sec);
421
422		int year  = 0;
423		int month = 0;
424		int day   = 0;
425
426		if (dateStamp.NotEmpty ())
427			{
428
429			sscanf (dateStamp.Get (),
430				    "%d:%d:%d",
431				    &year,
432				    &month,
433				    &day);
434
435			}
436
437		if (year  >= 1 && year  <= 9999 &&
438			month >= 1 && month <=   12 &&
439			day   >= 1 && day   <=   31)
440			{
441
442			sprintf (s,
443					 "%04d-%02d-%02dT%02u:%02u:%sZ",
444					 year,
445					 month,
446					 day,
447					 (unsigned) Round_uint32 (timeStamp [0].As_real64 ()),
448					 (unsigned) Round_uint32 (timeStamp [1].As_real64 ()),
449					 sec);
450
451			}
452
453		else
454			{
455
456			sprintf (s,
457					 "%02u:%02u:%sZ",
458					 (unsigned) Round_uint32 (timeStamp [0].As_real64 ()),
459					 (unsigned) Round_uint32 (timeStamp [1].As_real64 ()),
460					 sec);
461
462			}
463
464		result.Set (s);
465
466		}
467
468	return result;
469
470	}
471
472/*****************************************************************************/
473
474void dng_xmp::DecodeGPSDateTime (const dng_string &s,
475								 dng_string &dateStamp,
476								 dng_urational *timeStamp)
477	{
478
479	dateStamp.Clear ();
480
481	timeStamp [0].Clear ();
482	timeStamp [1].Clear ();
483	timeStamp [2].Clear ();
484
485	if (s.NotEmpty ())
486		{
487
488		unsigned year   = 0;
489		unsigned month  = 0;
490		unsigned day    = 0;
491		unsigned hour   = 0;
492		unsigned minute = 0;
493
494		double second = 0.0;
495
496		if (sscanf (s.Get (),
497					"%u-%u-%uT%u:%u:%lf",
498					&year,
499					&month,
500					&day,
501					&hour,
502					&minute,
503					&second) == 6)
504			{
505
506			if (year  >= 1 && year  <= 9999 &&
507				month >= 1 && month <= 12   &&
508				day   >= 1 && day   <= 31   )
509				{
510
511				char ss [64];
512
513				sprintf (ss,
514						 "%04u:%02u:%02u",
515						 year,
516						 month,
517						 day);
518
519				dateStamp.Set (ss);
520
521				}
522
523			}
524
525		else if (sscanf (s.Get (),
526						 "%u:%u:%lf",
527						 &hour,
528				 		 &minute,
529				 		 &second) != 3)
530			{
531
532			return;
533
534			}
535
536		timeStamp [0] = dng_urational ((uint32) hour  , 1);
537		timeStamp [1] = dng_urational ((uint32) minute, 1);
538
539		timeStamp [2].Set_real64 (second, 1000);
540
541		}
542
543	}
544
545/*****************************************************************************/
546
547void dng_xmp::Parse (dng_host &host,
548					 const void *buffer,
549				     uint32 count)
550	{
551
552	fSDK->Parse (host,
553				 (const char *) buffer,
554				 count);
555
556	}
557
558/*****************************************************************************/
559
560dng_memory_block * dng_xmp::Serialize (bool asPacket,
561									   uint32 targetBytes,
562									   uint32 padBytes,
563									   bool forJPEG,
564									   bool compact) const
565	{
566
567	return fSDK->Serialize (fAllocator,
568							asPacket,
569							targetBytes,
570							padBytes,
571							forJPEG,
572							compact);
573
574	}
575
576/*****************************************************************************/
577
578void dng_xmp::PackageForJPEG (AutoPtr<dng_memory_block> &stdBlock,
579							  AutoPtr<dng_memory_block> &extBlock,
580							  dng_string &extDigest) const
581	{
582
583	fSDK->PackageForJPEG (fAllocator,
584						  stdBlock,
585						  extBlock,
586						  extDigest);
587
588	}
589
590/*****************************************************************************/
591
592void dng_xmp::MergeFromJPEG (const dng_xmp &xmp)
593	{
594
595	fSDK->MergeFromJPEG (xmp.fSDK);
596
597	}
598
599/*****************************************************************************/
600
601bool dng_xmp::HasMeta () const
602	{
603
604	return fSDK->HasMeta ();
605
606	}
607
608/*****************************************************************************/
609
610void * dng_xmp::GetPrivateMeta ()
611	{
612
613	return fSDK->GetPrivateMeta ();
614
615	}
616
617/*****************************************************************************/
618
619bool dng_xmp::Exists (const char *ns,
620					  const char *path) const
621	{
622
623	return fSDK->Exists (ns, path);
624
625	}
626
627/*****************************************************************************/
628
629bool dng_xmp::HasNameSpace (const char *ns) const
630	{
631
632	return fSDK->HasNameSpace (ns);
633
634	}
635
636/*****************************************************************************/
637
638bool dng_xmp::IteratePaths (IteratePathsCallback *callback,
639						    void *callbackData,
640							const char *ns,
641							const char *path)
642	{
643
644	return fSDK->IteratePaths (callback, callbackData, ns, path);
645
646	}
647
648/*****************************************************************************/
649
650void dng_xmp::Remove (const char *ns,
651				      const char *path)
652	{
653
654	fSDK->Remove (ns, path);
655
656	}
657
658/*****************************************************************************/
659
660void dng_xmp::RemoveProperties (const char *ns)
661	{
662
663	fSDK->RemoveProperties (ns);
664
665	}
666
667/*****************************************************************************/
668
669void dng_xmp::RemoveEmptyStringOrArray (const char *ns,
670								        const char *path)
671	{
672
673	if (path == NULL || path [0] == 0)
674		{
675		return;
676		}
677
678	if (fSDK->IsEmptyString (ns, path) ||
679		fSDK->IsEmptyArray  (ns, path))
680		{
681
682		Remove (ns, path);
683
684		}
685
686	}
687
688/*****************************************************************************/
689
690static bool RemoveEmptyStringsAndArraysCallback (const char *ns,
691												 const char *path,
692												 void *callbackData)
693	{
694
695	dng_xmp *xmp = (dng_xmp *) callbackData;
696
697	xmp->RemoveEmptyStringOrArray (ns, path);
698
699	return true;
700
701	}
702
703/*****************************************************************************/
704
705void dng_xmp::RemoveEmptyStringsAndArrays (const char *ns)
706	{
707
708	IteratePaths (RemoveEmptyStringsAndArraysCallback,
709				  (void *) this,
710				  ns,
711				  NULL);
712
713	}
714
715/*****************************************************************************/
716
717void dng_xmp::Set (const char *ns,
718				   const char *path,
719				   const char *text)
720	{
721
722	fSDK->Set (ns, path, text);
723
724	}
725
726/*****************************************************************************/
727
728bool dng_xmp::GetString (const char *ns,
729						 const char *path,
730						 dng_string &s) const
731	{
732
733	return fSDK->GetString (ns, path, s);
734
735	}
736
737/*****************************************************************************/
738
739void dng_xmp::SetString (const char *ns,
740						 const char *path,
741						 const dng_string &s)
742	{
743
744	fSDK->SetString (ns, path, s);
745
746	}
747
748/*****************************************************************************/
749
750bool dng_xmp::SyncString (const char *ns,
751						  const char *path,
752						  dng_string &s,
753						  uint32 options)
754	{
755
756	bool isDefault = s.IsEmpty ();
757
758	// Sync 1: Force XMP to match non-XMP.
759
760	if (options & ignoreXMP)
761		{
762
763		if (isDefault || (options & removeXMP))
764			{
765
766			Remove (ns, path);
767
768			}
769
770		else
771			{
772
773			SetString (ns, path, s);
774
775			}
776
777		return false;
778
779		}
780
781	// Sync 2: From non-XMP to XMP if non-XMP is prefered.
782
783	if ((options & preferNonXMP) && !isDefault)
784		{
785
786		if (options & removeXMP)
787			{
788
789			Remove (ns, path);
790
791			}
792
793		else
794			{
795
796			SetString (ns, path, s);
797
798			}
799
800		return false;
801
802		}
803
804	// Sync 3: From XMP to non-XMP if XMP is prefered or default non-XMP.
805
806	if ((options & preferXMP) || isDefault)
807		{
808
809		if (GetString (ns, path, s))
810			{
811
812			if (options & removeXMP)
813				{
814
815				Remove (ns, path);
816
817				}
818
819			return true;
820
821			}
822
823		}
824
825	// Sync 4: From non-XMP to XMP.
826
827	if (options & removeXMP)
828		{
829
830		Remove (ns, path);
831
832		}
833
834	else if (!isDefault)
835		{
836
837		SetString (ns, path, s);
838
839		}
840
841	return false;
842
843	}
844
845/*****************************************************************************/
846
847bool dng_xmp::GetStringList (const char *ns,
848						 	 const char *path,
849						 	 dng_string_list &list) const
850	{
851
852	return fSDK->GetStringList (ns, path, list);
853
854	}
855
856/*****************************************************************************/
857
858void dng_xmp::SetStringList (const char *ns,
859						     const char *path,
860						     const dng_string_list &list,
861						     bool isBag)
862	{
863
864	fSDK->SetStringList (ns, path, list, isBag);
865
866	}
867
868/*****************************************************************************/
869
870void dng_xmp::SyncStringList (const char *ns,
871						      const char *path,
872						      dng_string_list &list,
873						      bool isBag,
874						      uint32 options)
875	{
876
877	bool isDefault = (list.Count () == 0);
878
879	// First make sure the XMP is not badly formatted, since
880	// this breaks some Photoshop logic.
881
882	ValidateStringList (ns, path);
883
884	// Sync 1: Force XMP to match non-XMP.
885
886	if (options & ignoreXMP)
887		{
888
889		if (isDefault)
890			{
891
892			Remove (ns, path);
893
894			}
895
896		else
897			{
898
899			SetStringList (ns, path, list, isBag);
900
901			}
902
903		return;
904
905		}
906
907	// Sync 2: From non-XMP to XMP if non-XMP is prefered.
908
909	if ((options & preferNonXMP) && !isDefault)
910		{
911
912		SetStringList (ns, path, list, isBag);
913
914		return;
915
916		}
917
918	// Sync 3: From XMP to non-XMP if XMP is prefered or default non-XMP.
919
920	if ((options & preferXMP) || isDefault)
921		{
922
923		if (GetStringList (ns, path, list))
924			{
925
926			return;
927
928			}
929
930		}
931
932	// Sync 4: From non-XMP to XMP.
933
934	if (!isDefault)
935		{
936
937		SetStringList (ns, path, list, isBag);
938
939		}
940
941	}
942
943/*****************************************************************************/
944
945void dng_xmp::SetStructField (const char *ns,
946							  const char *path,
947							  const char *fieldNS,
948							  const char *fieldName,
949							  const dng_string &s)
950	{
951
952	dng_string ss (s);
953
954	ss.SetLineEndings ('\n');
955
956	ss.StripLowASCII ();
957
958	fSDK->SetStructField (ns, path, fieldNS, fieldName, ss.Get ());
959
960	}
961
962/*****************************************************************************/
963
964void dng_xmp::SetStructField (const char *ns,
965							  const char *path,
966							  const char *fieldNS,
967							  const char *fieldName,
968							  const char *s)
969	{
970
971	fSDK->SetStructField (ns, path, fieldNS, fieldName, s);
972
973	}
974
975/*****************************************************************************/
976
977void dng_xmp::DeleteStructField (const char *ns,
978								 const char *path,
979								 const char *fieldNS,
980								 const char *fieldName)
981	{
982
983	fSDK->DeleteStructField (ns, path, fieldNS, fieldName);
984
985	}
986
987/*****************************************************************************/
988
989bool dng_xmp::GetStructField (const char *ns,
990							  const char *path,
991							  const char *fieldNS,
992							  const char *fieldName,
993							  dng_string &s) const
994	{
995
996	return fSDK->GetStructField (ns, path, fieldNS, fieldName, s);
997
998	}
999
1000/*****************************************************************************/
1001
1002void dng_xmp::SetAltLangDefault (const char *ns,
1003								 const char *path,
1004								 const dng_string &s)
1005	{
1006
1007	fSDK->SetAltLangDefault (ns, path, s);
1008
1009	}
1010
1011/*****************************************************************************/
1012
1013bool dng_xmp::GetAltLangDefault (const char *ns,
1014								 const char *path,
1015								 dng_string &s) const
1016	{
1017
1018	return fSDK->GetAltLangDefault (ns, path, s);
1019
1020	}
1021
1022/*****************************************************************************/
1023
1024bool dng_xmp::SyncAltLangDefault (const char *ns,
1025								  const char *path,
1026								  dng_string &s,
1027								  uint32 options)
1028	{
1029
1030	bool isDefault = s.IsEmpty ();
1031
1032	// Sync 1: Force XMP to match non-XMP.
1033
1034	if (options & ignoreXMP)
1035		{
1036
1037		if (isDefault)
1038			{
1039
1040			Remove (ns, path);
1041
1042			}
1043
1044		else
1045			{
1046
1047			SetAltLangDefault (ns, path, s);
1048
1049			}
1050
1051		return false;
1052
1053		}
1054
1055	// Sync 2: From non-XMP to XMP if non-XMP is prefered.
1056
1057	if ((options & preferNonXMP) && !isDefault)
1058		{
1059
1060		SetAltLangDefault (ns, path, s);
1061
1062		return false;
1063
1064		}
1065
1066	// Sync 3: From XMP to non-XMP if XMP is prefered or default non-XMP.
1067
1068	if ((options & preferXMP) || isDefault)
1069		{
1070
1071		if (GetAltLangDefault (ns, path, s))
1072			{
1073
1074			return true;
1075
1076			}
1077
1078		}
1079
1080	// Sync 4: From non-XMP to XMP.
1081
1082	if (!isDefault)
1083		{
1084
1085		SetAltLangDefault (ns, path, s);
1086
1087		}
1088
1089	return false;
1090
1091	}
1092
1093/*****************************************************************************/
1094
1095bool dng_xmp::GetBoolean (const char *ns,
1096					 	  const char *path,
1097					 	  bool &x) const
1098	{
1099
1100	dng_string s;
1101
1102	if (GetString (ns, path, s))
1103		{
1104
1105		if (s.Matches ("True"))
1106			{
1107
1108			x = true;
1109
1110			return true;
1111
1112			}
1113
1114		if (s.Matches ("False"))
1115			{
1116
1117			x = false;
1118
1119			return true;
1120
1121			}
1122
1123		}
1124
1125	return false;
1126
1127	}
1128
1129/*****************************************************************************/
1130
1131void dng_xmp::SetBoolean (const char *ns,
1132					 	  const char *path,
1133					 	  bool x)
1134	{
1135
1136	Set (ns, path, x ? "True" : "False");
1137
1138	}
1139
1140/*****************************************************************************/
1141
1142bool dng_xmp::Get_int32 (const char *ns,
1143						 const char *path,
1144						 int32 &x) const
1145	{
1146
1147	dng_string s;
1148
1149	if (GetString (ns, path, s))
1150		{
1151
1152		if (s.NotEmpty ())
1153			{
1154
1155			int y = 0;
1156
1157			if (sscanf (s.Get (), "%d", &y) == 1)
1158				{
1159
1160				x = y;
1161
1162				return true;
1163
1164				}
1165
1166			}
1167
1168		}
1169
1170	return false;
1171
1172	}
1173
1174/*****************************************************************************/
1175
1176void dng_xmp::Set_int32 (const char *ns,
1177						 const char *path,
1178						 int32 x,
1179						 bool usePlus)
1180	{
1181
1182	char s [64];
1183
1184	if (x > 0 && usePlus)
1185		{
1186		sprintf (s, "+%d", (int) x);
1187		}
1188	else
1189		{
1190		sprintf (s, "%d", (int) x);
1191		}
1192
1193	Set (ns, path, s);
1194
1195	}
1196
1197/*****************************************************************************/
1198
1199bool dng_xmp::Get_uint32 (const char *ns,
1200					 	  const char *path,
1201					 	  uint32 &x) const
1202	{
1203
1204	dng_string s;
1205
1206	if (GetString (ns, path, s))
1207		{
1208
1209		if (s.NotEmpty ())
1210			{
1211
1212			unsigned y = 0;
1213
1214			if (sscanf (s.Get (), "%u", &y) == 1)
1215				{
1216
1217				x = y;
1218
1219				return true;
1220
1221				}
1222
1223			}
1224
1225		}
1226
1227	return false;
1228
1229	}
1230
1231/*****************************************************************************/
1232
1233void dng_xmp::Set_uint32 (const char *ns,
1234						  const char *path,
1235						  uint32 x)
1236	{
1237
1238	char s [64];
1239
1240	sprintf (s,
1241			 "%u",
1242			 (unsigned) x);
1243
1244	Set (ns, path, s);
1245
1246	}
1247
1248/*****************************************************************************/
1249
1250void dng_xmp::Sync_uint32 (const char *ns,
1251						   const char *path,
1252						   uint32 &x,
1253						   bool isDefault,
1254						   uint32 options)
1255	{
1256
1257	// Sync 1: Force XMP to match non-XMP.
1258
1259	if (options & ignoreXMP)
1260		{
1261
1262		if (isDefault || (options & removeXMP))
1263			{
1264
1265			Remove (ns, path);
1266
1267			}
1268
1269		else
1270			{
1271
1272			Set_uint32 (ns, path, x);
1273
1274			}
1275
1276		return;
1277
1278		}
1279
1280	// Sync 2: From non-XMP to XMP if non-XMP is prefered.
1281
1282	if ((options & preferNonXMP) && !isDefault)
1283		{
1284
1285		if (options & removeXMP)
1286			{
1287
1288			Remove (ns, path);
1289
1290			}
1291
1292		else
1293			{
1294
1295			Set_uint32 (ns, path, x);
1296
1297			}
1298
1299		return;
1300
1301		}
1302
1303	// Sync 3: From XMP to non-XMP if XMP is prefered or default non-XMP.
1304
1305	if ((options & preferXMP) || isDefault)
1306		{
1307
1308		if (Get_uint32 (ns, path, x))
1309			{
1310
1311			if (options & removeXMP)
1312				{
1313
1314				Remove (ns, path);
1315
1316				}
1317
1318			return;
1319
1320			}
1321
1322		}
1323
1324	// Sync 4: From non-XMP to XMP.
1325
1326	if (options & removeXMP)
1327		{
1328
1329		Remove (ns, path);
1330
1331		}
1332
1333	else if (!isDefault)
1334		{
1335
1336		Set_uint32 (ns, path, x);
1337
1338		}
1339
1340	}
1341
1342/*****************************************************************************/
1343
1344void dng_xmp::Sync_uint32_array (const char *ns,
1345						   		 const char *path,
1346						   		 uint32 *data,
1347						   		 uint32 &count,
1348						   		 uint32 maxCount,
1349						   		 uint32 options)
1350	{
1351
1352	dng_string_list list;
1353
1354	for (uint32 j = 0; j < count; j++)
1355		{
1356
1357		char s [32];
1358
1359		sprintf (s, "%u", (unsigned) data [j]);
1360
1361		dng_string ss;
1362
1363		ss.Set (s);
1364
1365		list.Append (ss);
1366
1367		}
1368
1369	SyncStringList (ns,
1370					path,
1371					list,
1372					false,
1373					options);
1374
1375	count = 0;
1376
1377	for (uint32 k = 0; k < maxCount; k++)
1378		{
1379
1380		data [k] = 0;
1381
1382		if (k < list.Count ())
1383			{
1384
1385			unsigned x = 0;
1386
1387			if (sscanf (list [k].Get (), "%u", &x) == 1)
1388				{
1389
1390				data [count++] = x;
1391
1392				}
1393
1394			}
1395
1396		}
1397
1398	}
1399
1400/*****************************************************************************/
1401
1402bool dng_xmp::Get_real64 (const char *ns,
1403					  	  const char *path,
1404					  	  real64 &x) const
1405	{
1406
1407	dng_string s;
1408
1409	if (GetString (ns, path, s))
1410		{
1411
1412		if (s.NotEmpty ())
1413			{
1414
1415			double y = 0;
1416
1417			if (sscanf (s.Get (), "%lf", &y) == 1)
1418				{
1419
1420				x = y;
1421
1422				return true;
1423
1424				}
1425
1426			}
1427
1428		}
1429
1430	return false;
1431
1432	}
1433
1434/*****************************************************************************/
1435
1436void dng_xmp::Set_real64 (const char *ns,
1437					  	  const char *path,
1438					  	  real64 x,
1439					      uint32 places,
1440					      bool trim,
1441					      bool usePlus)
1442	{
1443
1444	char s [64];
1445
1446	if (x > 0.0 && usePlus)
1447		{
1448		sprintf (s, "+%0.*f", (unsigned) places, (double) x);
1449		}
1450	else
1451		{
1452		sprintf (s, "%0.*f", (unsigned) places, (double) x);
1453		}
1454
1455	if (trim)
1456		{
1457
1458		while (s [strlen (s) - 1] == '0')
1459			{
1460			s [strlen (s) - 1] = 0;
1461			}
1462
1463		if (s [strlen (s) - 1] == '.')
1464			{
1465			s [strlen (s) - 1] = 0;
1466			}
1467
1468		}
1469
1470	Set (ns, path, s);
1471
1472	}
1473
1474/*****************************************************************************/
1475
1476bool dng_xmp::Get_urational (const char *ns,
1477							 const char *path,
1478							 dng_urational &r) const
1479	{
1480
1481	dng_string s;
1482
1483	if (GetString (ns, path, s))
1484		{
1485
1486		if (s.NotEmpty ())
1487			{
1488
1489			unsigned n = 0;
1490			unsigned d = 0;
1491
1492			if (sscanf (s.Get (), "%u/%u", &n, &d) == 2)
1493				{
1494
1495				if (d != 0)
1496					{
1497
1498					r = dng_urational (n, d);
1499
1500					return true;
1501
1502					}
1503
1504				}
1505
1506			}
1507
1508		}
1509
1510	return false;
1511
1512	}
1513
1514/*****************************************************************************/
1515
1516void dng_xmp::Set_urational (const char *ns,
1517							 const char *path,
1518							 const dng_urational &r)
1519	{
1520
1521	char s [64];
1522
1523	sprintf (s,
1524			 "%u/%u",
1525			 (unsigned) r.n,
1526			 (unsigned) r.d);
1527
1528	Set (ns, path, s);
1529
1530	}
1531
1532/*****************************************************************************/
1533
1534void dng_xmp::Sync_urational (const char *ns,
1535							  const char *path,
1536							  dng_urational &r,
1537							  uint32 options)
1538	{
1539
1540	bool isDefault = r.NotValid ();
1541
1542	// Sync 1: Force XMP to match non-XMP.
1543
1544	if (options & ignoreXMP)
1545		{
1546
1547		if (isDefault || (options & removeXMP))
1548			{
1549
1550			Remove (ns, path);
1551
1552			}
1553
1554		else
1555			{
1556
1557			Set_urational (ns, path, r);
1558
1559			}
1560
1561		return;
1562
1563		}
1564
1565	// Sync 2: From non-XMP to XMP if non-XMP is prefered.
1566
1567	if ((options & preferNonXMP) && !isDefault)
1568		{
1569
1570		if (options & removeXMP)
1571			{
1572
1573			Remove (ns, path);
1574
1575			}
1576
1577		else
1578			{
1579
1580			Set_urational (ns, path, r);
1581
1582			}
1583
1584		return;
1585
1586		}
1587
1588	// Sync 3: From XMP to non-XMP if XMP is prefered or default non-XMP.
1589
1590	if ((options & preferXMP) || isDefault)
1591		{
1592
1593		if (Get_urational (ns, path, r))
1594			{
1595
1596			if (options & removeXMP)
1597				{
1598
1599				Remove (ns, path);
1600
1601				}
1602
1603			return;
1604
1605			}
1606
1607		}
1608
1609	// Sync 4: From non-XMP to XMP.
1610
1611	if (options & removeXMP)
1612		{
1613
1614		Remove (ns, path);
1615
1616		}
1617
1618	else if (!isDefault)
1619		{
1620
1621		Set_urational (ns, path, r);
1622
1623		}
1624
1625	}
1626
1627/*****************************************************************************/
1628
1629bool dng_xmp::Get_srational (const char *ns,
1630							 const char *path,
1631							 dng_srational &r) const
1632	{
1633
1634	dng_string s;
1635
1636	if (GetString (ns, path, s))
1637		{
1638
1639		if (s.NotEmpty ())
1640			{
1641
1642			int n = 0;
1643			int d = 0;
1644
1645			if (sscanf (s.Get (), "%d/%d", &n, &d) == 2)
1646				{
1647
1648				if (d != 0)
1649					{
1650
1651					r = dng_srational (n, d);
1652
1653					return true;
1654
1655					}
1656
1657				}
1658
1659			}
1660
1661		}
1662
1663	return false;
1664
1665	}
1666
1667/*****************************************************************************/
1668
1669void dng_xmp::Set_srational (const char *ns,
1670							 const char *path,
1671							 const dng_srational &r)
1672	{
1673
1674	char s [64];
1675
1676	sprintf (s,
1677			 "%d/%d",
1678			 (int) r.n,
1679			 (int) r.d);
1680
1681	Set (ns, path, s);
1682
1683	}
1684
1685/*****************************************************************************/
1686
1687void dng_xmp::Sync_srational (const char *ns,
1688							  const char *path,
1689							  dng_srational &r,
1690							  uint32 options)
1691	{
1692
1693	bool isDefault = r.NotValid ();
1694
1695	// Sync 1: Force XMP to match non-XMP.
1696
1697	if (options & ignoreXMP)
1698		{
1699
1700		if (isDefault || (options & removeXMP))
1701			{
1702
1703			Remove (ns, path);
1704
1705			}
1706
1707		else
1708			{
1709
1710			Set_srational (ns, path, r);
1711
1712			}
1713
1714		return;
1715
1716		}
1717
1718	// Sync 2: From non-XMP to XMP if non-XMP is prefered.
1719
1720	if ((options & preferNonXMP) && !isDefault)
1721		{
1722
1723		if (options & removeXMP)
1724			{
1725
1726			Remove (ns, path);
1727
1728			}
1729
1730		else
1731			{
1732
1733			Set_srational (ns, path, r);
1734
1735			}
1736
1737		return;
1738
1739		}
1740
1741	// Sync 3: From XMP to non-XMP if XMP is prefered or default non-XMP.
1742
1743	if ((options & preferXMP) || isDefault)
1744		{
1745
1746		if (Get_srational (ns, path, r))
1747			{
1748
1749			if (options & removeXMP)
1750				{
1751
1752				Remove (ns, path);
1753
1754				}
1755
1756			return;
1757
1758			}
1759
1760		}
1761
1762	// Sync 4: From non-XMP to XMP.
1763
1764	if (options & removeXMP)
1765		{
1766
1767		Remove (ns, path);
1768
1769		}
1770
1771	else if (!isDefault)
1772		{
1773
1774		Set_srational (ns, path, r);
1775
1776		}
1777
1778	}
1779
1780/*****************************************************************************/
1781
1782bool dng_xmp::GetFingerprint (const char *ns,
1783					 		  const char *path,
1784					    	  dng_fingerprint &print) const
1785	{
1786
1787	dng_string s;
1788
1789	if (GetString (ns, path, s))
1790		{
1791
1792		dng_fingerprint temp = DecodeFingerprint (s);
1793
1794		if (temp.IsValid ())
1795			{
1796
1797			print = temp;
1798
1799			return true;
1800
1801			}
1802
1803		}
1804
1805	return false;
1806
1807	}
1808
1809/******************************************************************************/
1810
1811void dng_xmp::SetFingerprint (const char *ns,
1812							  const char *tag,
1813							  const dng_fingerprint &print,
1814							  bool allowInvalid)
1815	{
1816
1817	dng_string s = EncodeFingerprint (print, allowInvalid);
1818
1819	if (s.IsEmpty ())
1820		{
1821
1822		Remove (ns, tag);
1823
1824		}
1825
1826	else
1827		{
1828
1829		SetString (ns, tag, s);
1830
1831		}
1832
1833	}
1834
1835/******************************************************************************/
1836
1837void dng_xmp::SetVersion2to4 (const char *ns,
1838							  const char *path,
1839							  uint32 version)
1840	{
1841
1842	char buf [32];
1843
1844	if (version & 0x000000ff)
1845		{
1846
1847		// x.x.x.x
1848
1849		sprintf (buf,
1850				 "%u.%u.%u.%u",
1851				 (unsigned) ((version >> 24) & 0xff),
1852				 (unsigned) ((version >> 16) & 0xff),
1853				 (unsigned) ((version >>  8) & 0xff),
1854				 (unsigned) ((version	   ) & 0xff));
1855
1856		}
1857
1858	else if (version & 0x0000ff00)
1859		{
1860
1861		// x.x.x
1862
1863		sprintf (buf,
1864				 "%u.%u.%u",
1865				 (unsigned) ((version >> 24) & 0xff),
1866				 (unsigned) ((version >> 16) & 0xff),
1867				 (unsigned) ((version >>  8) & 0xff));
1868
1869		}
1870
1871	else
1872		{
1873
1874		// x.x
1875
1876		sprintf (buf,
1877				 "%u.%u",
1878				 (unsigned) ((version >> 24) & 0xff),
1879				 (unsigned) ((version >> 16) & 0xff));
1880
1881		}
1882
1883	Set (ns, path, buf);
1884
1885	}
1886
1887/******************************************************************************/
1888
1889dng_fingerprint dng_xmp::GetIPTCDigest () const
1890	{
1891
1892	dng_fingerprint digest;
1893
1894	if (GetFingerprint (XMP_NS_PHOTOSHOP,
1895						"LegacyIPTCDigest",
1896						digest))
1897		{
1898
1899		return digest;
1900
1901		}
1902
1903	return dng_fingerprint ();
1904
1905	}
1906
1907/******************************************************************************/
1908
1909void dng_xmp::SetIPTCDigest (dng_fingerprint &digest)
1910	{
1911
1912	SetFingerprint (XMP_NS_PHOTOSHOP,
1913					"LegacyIPTCDigest",
1914					digest);
1915
1916	}
1917
1918/******************************************************************************/
1919
1920void dng_xmp::ClearIPTCDigest ()
1921	{
1922
1923	Remove (XMP_NS_PHOTOSHOP, "LegacyIPTCDigest");
1924
1925	}
1926
1927/*****************************************************************************/
1928
1929void dng_xmp::SyncIPTC (dng_iptc &iptc,
1930					    uint32 options)
1931	{
1932
1933	SyncAltLangDefault (XMP_NS_DC,
1934						"title",
1935						iptc.fTitle,
1936						options);
1937
1938	SyncString (XMP_NS_PHOTOSHOP,
1939				"Category",
1940				iptc.fCategory,
1941				options);
1942
1943		{
1944
1945		uint32 x = 0xFFFFFFFF;
1946
1947		if (iptc.fUrgency >= 0)
1948			{
1949
1950			x = (uint32) iptc.fUrgency;
1951
1952			}
1953
1954		Sync_uint32 (XMP_NS_PHOTOSHOP,
1955					 "Urgency",
1956					 x,
1957					 x == 0xFFFFFFFF,
1958					 options);
1959
1960		if (x <= 9)
1961			{
1962
1963			iptc.fUrgency = (int32) x;
1964
1965			}
1966
1967		}
1968
1969	SyncStringList (XMP_NS_PHOTOSHOP,
1970					"SupplementalCategories",
1971					iptc.fSupplementalCategories,
1972					true,
1973					options);
1974
1975	SyncStringList (XMP_NS_PHOTOSHOP,
1976					"Keywords",
1977					iptc.fKeywords,
1978					true,
1979					options);
1980
1981	SyncString (XMP_NS_PHOTOSHOP,
1982			    "Instructions",
1983			    iptc.fInstructions,
1984			    options);
1985
1986		{
1987
1988		dng_string s = iptc.fDateTimeCreated.Encode_ISO_8601 ();
1989
1990		if (SyncString (XMP_NS_PHOTOSHOP,
1991						"DateCreated",
1992						s,
1993						options))
1994			{
1995
1996			iptc.fDateTimeCreated.Decode_ISO_8601 (s.Get ());
1997
1998			}
1999
2000		}
2001
2002		{
2003
2004		dng_string s = iptc.fDigitalCreationDateTime.Encode_ISO_8601 ();
2005
2006		if (SyncString (XMP_NS_EXIF,
2007						"DateTimeDigitized",
2008						s,
2009						options))
2010			{
2011
2012			iptc.fDigitalCreationDateTime.Decode_ISO_8601 (s.Get ());
2013
2014			}
2015
2016		}
2017
2018	SyncStringList (XMP_NS_DC,
2019			        "creator",
2020			        iptc.fAuthors,
2021					false,
2022					options);
2023
2024	SyncString (XMP_NS_PHOTOSHOP,
2025			    "AuthorsPosition",
2026			    iptc.fAuthorsPosition,
2027			    options);
2028
2029	SyncString (XMP_NS_PHOTOSHOP,
2030			    "City",
2031			    iptc.fCity,
2032			    options);
2033
2034	SyncString (XMP_NS_PHOTOSHOP,
2035			    "State",
2036			    iptc.fState,
2037			    options);
2038
2039	SyncString (XMP_NS_PHOTOSHOP,
2040			    "Country",
2041			    iptc.fCountry,
2042			    options);
2043
2044	SyncString (XMP_NS_IPTC,
2045			    "CountryCode",
2046			    iptc.fCountryCode,
2047			    options);
2048
2049	SyncString (XMP_NS_IPTC,
2050			    "Location",
2051			    iptc.fLocation,
2052			    options);
2053
2054	SyncString (XMP_NS_PHOTOSHOP,
2055			    "TransmissionReference",
2056			    iptc.fTransmissionReference,
2057			    options);
2058
2059	SyncString (XMP_NS_PHOTOSHOP,
2060			    "Headline",
2061			    iptc.fHeadline,
2062			    options);
2063
2064	SyncString (XMP_NS_PHOTOSHOP,
2065			    "Credit",
2066			    iptc.fCredit,
2067			    options);
2068
2069	SyncString (XMP_NS_PHOTOSHOP,
2070			    "Source",
2071			    iptc.fSource,
2072			    options);
2073
2074	SyncAltLangDefault (XMP_NS_DC,
2075						"rights",
2076						iptc.fCopyrightNotice,
2077						options);
2078
2079	SyncAltLangDefault (XMP_NS_DC,
2080						"description",
2081						iptc.fDescription,
2082						options);
2083
2084	SyncString (XMP_NS_PHOTOSHOP,
2085			    "CaptionWriter",
2086			    iptc.fDescriptionWriter,
2087			    options);
2088
2089	}
2090
2091/*****************************************************************************/
2092
2093void dng_xmp::IngestIPTC (dng_metadata &metadata,
2094					      bool xmpIsNewer)
2095	{
2096
2097	if (metadata.IPTCLength ())
2098		{
2099
2100		// Parse the IPTC block.
2101
2102		dng_iptc iptc;
2103
2104		iptc.Parse (metadata.IPTCData   (),
2105					metadata.IPTCLength (),
2106					metadata.IPTCOffset ());
2107
2108		// Compute fingerprint of IPTC data both ways, including and
2109		// excluding the padding data.
2110
2111		dng_fingerprint iptcDigest1 = metadata.IPTCDigest (true );
2112		dng_fingerprint iptcDigest2 = metadata.IPTCDigest (false);
2113
2114		// See if there is an IPTC fingerprint stored in the XMP.
2115
2116		dng_fingerprint xmpDigest = GetIPTCDigest ();
2117
2118		if (xmpDigest.IsValid ())
2119			{
2120
2121			// If they match, the XMP was already synced with this
2122			// IPTC block, and we should not resync since it might
2123			// overwrite changes in the XMP data.
2124
2125			if (iptcDigest1 == xmpDigest)
2126				{
2127
2128				return;
2129
2130				}
2131
2132			// If it matches the incorrectly computed digest, skip
2133			// the sync, but fix the digest in the XMP.
2134
2135			if (iptcDigest2 == xmpDigest)
2136				{
2137
2138				SetIPTCDigest (iptcDigest1);
2139
2140				return;
2141
2142				}
2143
2144			// Else the IPTC has changed, so force an update.
2145
2146			xmpIsNewer = false;
2147
2148			}
2149
2150		else
2151			{
2152
2153			// There is no IPTC digest.  Previously we would
2154			// prefer the IPTC in this case, but the MWG suggests
2155			// that we prefer the XMP in this case.
2156
2157			xmpIsNewer = true;
2158
2159			}
2160
2161		// Remember the fingerprint of the IPTC we are syncing with.
2162
2163		SetIPTCDigest (iptcDigest1);
2164
2165		// Find the sync options.
2166
2167		uint32 options = xmpIsNewer ? preferXMP
2168									: preferNonXMP;
2169
2170		// Synchronize the fields.
2171
2172		SyncIPTC (iptc, options);
2173
2174		}
2175
2176	// After the IPTC data is moved to XMP, we don't need it anymore.
2177
2178	metadata.ClearIPTC ();
2179
2180	}
2181
2182/*****************************************************************************/
2183
2184void dng_xmp::RebuildIPTC (dng_metadata &metadata,
2185						   dng_memory_allocator &allocator,
2186						   bool padForTIFF)
2187	{
2188
2189	// If there is no XMP, then there is no IPTC.
2190
2191	if (!fSDK->HasMeta ())
2192		{
2193		return;
2194		}
2195
2196	// Extract the legacy IPTC fields from the XMP data.
2197
2198	dng_iptc iptc;
2199
2200	SyncIPTC (iptc, preferXMP);
2201
2202	// Build legacy IPTC record
2203
2204	if (iptc.NotEmpty ())
2205		{
2206
2207		AutoPtr<dng_memory_block> block (iptc.Spool (allocator,
2208													 padForTIFF));
2209
2210		metadata.SetIPTC (block);
2211
2212		}
2213
2214	}
2215
2216/*****************************************************************************/
2217
2218void dng_xmp::SyncFlash (uint32 &flashState,
2219						 uint32 &flashMask,
2220						 uint32 options)
2221	{
2222
2223	bool isDefault = (flashState == 0xFFFFFFFF);
2224
2225	if ((options & ignoreXMP) || !isDefault)
2226		{
2227
2228		Remove (XMP_NS_EXIF, "Flash");
2229
2230		}
2231
2232	if (!isDefault)
2233		{
2234
2235		fSDK->SetStructField (XMP_NS_EXIF,
2236							  "Flash",
2237							  XMP_NS_EXIF,
2238							  "Fired",
2239							  (flashState & 0x1) ? "True" : "False");
2240
2241		if (((flashMask >> 1) & 3) == 3)
2242			{
2243
2244			char s [8];
2245
2246			sprintf (s, "%u", (unsigned) ((flashState >> 1) & 3));
2247
2248			fSDK->SetStructField (XMP_NS_EXIF,
2249								  "Flash",
2250								  XMP_NS_EXIF,
2251								  "Return",
2252								  s);
2253
2254			}
2255
2256		if (((flashMask >> 3) & 3) == 3)
2257			{
2258
2259			char s [8];
2260
2261			sprintf (s, "%u", (unsigned) ((flashState >> 3) & 3));
2262
2263			fSDK->SetStructField (XMP_NS_EXIF,
2264								  "Flash",
2265								  XMP_NS_EXIF,
2266								  "Mode",
2267								  s);
2268
2269			}
2270
2271		if ((flashMask & (1 << 5)) != 0)
2272			{
2273
2274			fSDK->SetStructField (XMP_NS_EXIF,
2275								  "Flash",
2276								  XMP_NS_EXIF,
2277								  "Function",
2278								  (flashState & (1 << 5)) ? "True" : "False");
2279
2280			}
2281
2282		if ((flashMask & (1 << 6)) != 0)
2283			{
2284
2285			fSDK->SetStructField (XMP_NS_EXIF,
2286								  "Flash",
2287								  XMP_NS_EXIF,
2288								  "RedEyeMode",
2289								  (flashState & (1 << 6)) ? "True" : "False");
2290
2291			}
2292
2293		}
2294
2295	else if (fSDK->Exists (XMP_NS_EXIF, "Flash"))
2296		{
2297
2298		dng_string s;
2299
2300		if (fSDK->GetStructField (XMP_NS_EXIF,
2301								  "Flash",
2302								  XMP_NS_EXIF,
2303								  "Fired",
2304								  s))
2305			{
2306
2307			flashState = 0;
2308			flashMask  = 1;
2309
2310			if (s.Matches ("True"))
2311				{
2312				flashState |= 1;
2313				}
2314
2315			if (fSDK->GetStructField (XMP_NS_EXIF,
2316									  "Flash",
2317									  XMP_NS_EXIF,
2318									  "Return",
2319									  s))
2320				{
2321
2322				unsigned x = 0;
2323
2324				if (sscanf (s.Get (), "%u", &x) == 1 && x <= 3)
2325					{
2326
2327					flashState |= x << 1;
2328					flashMask  |= 3 << 1;
2329
2330					}
2331
2332				}
2333
2334			if (fSDK->GetStructField (XMP_NS_EXIF,
2335									  "Flash",
2336									  XMP_NS_EXIF,
2337									  "Mode",
2338									  s))
2339				{
2340
2341				unsigned x = 0;
2342
2343				if (sscanf (s.Get (), "%u", &x) == 1 && x <= 3)
2344					{
2345
2346					flashState |= x << 3;
2347					flashMask  |= 3 << 3;
2348
2349					}
2350
2351				}
2352
2353			if (fSDK->GetStructField (XMP_NS_EXIF,
2354									  "Flash",
2355									  XMP_NS_EXIF,
2356									  "Function",
2357									  s))
2358				{
2359
2360				flashMask |= 1 << 5;
2361
2362				if (s.Matches ("True"))
2363					{
2364					flashState |= 1 << 5;
2365					}
2366
2367				}
2368
2369			if (fSDK->GetStructField (XMP_NS_EXIF,
2370									  "Flash",
2371									  XMP_NS_EXIF,
2372									  "RedEyeMode",
2373									  s))
2374				{
2375
2376				flashMask |= 1 << 6;
2377
2378				if (s.Matches ("True"))
2379					{
2380					flashState |= 1 << 6;
2381					}
2382
2383				}
2384
2385			}
2386
2387		}
2388
2389	}
2390
2391/*****************************************************************************/
2392
2393void dng_xmp::SyncExif (dng_exif &exif,
2394						const dng_exif *originalExif,
2395						bool doingUpdateFromXMP,
2396						bool removeFromXMP)
2397	{
2398
2399	DNG_ASSERT (!doingUpdateFromXMP || originalExif,
2400				"Must have original EXIF if doingUpdateFromXMP");
2401
2402	// Default synchronization options for the read-only fields.
2403
2404	uint32 readOnly = doingUpdateFromXMP ? ignoreXMP
2405								         : preferNonXMP;
2406
2407	// Option for removable fields.
2408
2409	uint32 removable = removeFromXMP ? removeXMP
2410									 : 0;
2411
2412	// Make:
2413
2414	SyncString (XMP_NS_TIFF,
2415				"Make",
2416				exif.fMake,
2417				readOnly + removable);
2418
2419	// Model:
2420
2421	SyncString (XMP_NS_TIFF,
2422			    "Model",
2423			    exif.fModel,
2424			    readOnly + removable);
2425
2426	// Exif version number:
2427
2428		{
2429
2430		dng_string exifVersion;
2431
2432		if (exif.fExifVersion)
2433			{
2434
2435			unsigned b0 = ((exif.fExifVersion >> 24) & 0x0FF) - '0';
2436			unsigned b1 = ((exif.fExifVersion >> 16) & 0x0FF) - '0';
2437			unsigned b2 = ((exif.fExifVersion >>  8) & 0x0FF) - '0';
2438			unsigned b3 = ((exif.fExifVersion      ) & 0x0FF) - '0';
2439
2440			if (b0 <= 9 && b1 <= 9 && b2 <= 9 && b3 <= 9)
2441				{
2442
2443				char s [5];
2444
2445				sprintf (s,
2446						 "%1u%1u%1u%1u",
2447						 b0,
2448						 b1,
2449						 b2,
2450						 b3);
2451
2452				exifVersion.Set (s);
2453
2454				}
2455
2456			}
2457
2458		SyncString (XMP_NS_EXIF,
2459					"ExifVersion",
2460					exifVersion,
2461					readOnly);
2462
2463		if (exifVersion.NotEmpty ())
2464			{
2465
2466			unsigned b0;
2467			unsigned b1;
2468			unsigned b2;
2469			unsigned b3;
2470
2471			if (sscanf (exifVersion.Get (),
2472						"%1u%1u%1u%1u",
2473						&b0,
2474						&b1,
2475						&b2,
2476						&b3) == 4)
2477				{
2478
2479				if (b0 <= 9 && b1 <= 9 && b2 <= 9 && b3 <= 9)
2480					{
2481
2482					b0 += '0';
2483					b1 += '0';
2484					b2 += '0';
2485					b3 += '0';
2486
2487					exif.fExifVersion = (b0 << 24) |
2488										(b1 << 16) |
2489										(b2 <<  8) |
2490										(b3      );
2491
2492					}
2493
2494				}
2495
2496			}
2497
2498		// Provide default value for ExifVersion.
2499
2500		if (!exif.fExifVersion)
2501			{
2502
2503			exif.fExifVersion = DNG_CHAR4 ('0','2','2','1');
2504
2505			Set (XMP_NS_EXIF,
2506				 "ExifVersion",
2507				 "0221");
2508
2509			}
2510
2511		if (removeFromXMP)
2512			{
2513
2514			Remove (XMP_NS_EXIF, "ExifVersion");
2515
2516			}
2517
2518		}
2519
2520	// ExposureTime / ShutterSpeedValue:
2521
2522		{
2523
2524		// Process twice in case XMP contains only one of the
2525		// two fields.
2526
2527		for (uint32 pass = 0; pass < 2; pass++)
2528			{
2529
2530			dng_urational et = exif.fExposureTime;
2531
2532			Sync_urational (XMP_NS_EXIF,
2533							"ExposureTime",
2534							et,
2535							readOnly);
2536
2537			if (et.IsValid ())
2538				{
2539
2540				exif.SetExposureTime (et.As_real64 (), false);
2541
2542				}
2543
2544			dng_srational ss = exif.fShutterSpeedValue;
2545
2546			Sync_srational (XMP_NS_EXIF,
2547						    "ShutterSpeedValue",
2548						    ss,
2549						    readOnly);
2550
2551			if (ss.IsValid ())
2552				{
2553
2554				exif.SetShutterSpeedValue (ss.As_real64 ());
2555
2556				}
2557
2558			}
2559
2560		if (removeFromXMP)
2561			{
2562
2563			Remove (XMP_NS_EXIF, "ExposureTime");
2564
2565			Remove (XMP_NS_EXIF, "ShutterSpeedValue");
2566
2567			}
2568
2569		}
2570
2571	// FNumber / ApertureValue:
2572
2573		{
2574
2575		for (uint32 pass = 0; pass < 2; pass++)
2576			{
2577
2578			dng_urational fs = exif.fFNumber;
2579
2580			Sync_urational (XMP_NS_EXIF,
2581							"FNumber",
2582							fs,
2583							readOnly);
2584
2585			if (fs.IsValid ())
2586				{
2587
2588				exif.SetFNumber (fs.As_real64 ());
2589
2590				}
2591
2592			dng_urational av = exif.fApertureValue;
2593
2594			Sync_urational (XMP_NS_EXIF,
2595							"ApertureValue",
2596							av,
2597							readOnly);
2598
2599			if (av.IsValid ())
2600				{
2601
2602				exif.SetApertureValue (av.As_real64 ());
2603
2604				}
2605
2606			}
2607
2608		if (removeFromXMP)
2609			{
2610
2611			Remove (XMP_NS_EXIF, "FNumber");
2612
2613			Remove (XMP_NS_EXIF, "ApertureValue");
2614
2615			}
2616
2617		}
2618
2619	// Exposure program:
2620
2621	Sync_uint32 (XMP_NS_EXIF,
2622				 "ExposureProgram",
2623				 exif.fExposureProgram,
2624				 exif.fExposureProgram == 0xFFFFFFFF,
2625				 readOnly + removable);
2626
2627	// ISO Speed Ratings:
2628
2629		{
2630
2631		uint32 isoSpeedRatingsCount = 0;
2632
2633		uint32 isoSpeedRatingsOptions = readOnly;
2634
2635		uint32 oldISOSpeedRatings [3];
2636
2637		memcpy (oldISOSpeedRatings,
2638				exif.fISOSpeedRatings,
2639				sizeof (oldISOSpeedRatings));
2640
2641		bool checkXMPForHigherISO = false;
2642
2643		for (uint32 j = 0; j < 3; j++)
2644			{
2645
2646			// Special case: the EXIF 2.2x standard represents ISO speed ratings with
2647			// 2 bytes, which cannot hold ISO speed ratings above 65535 (e.g.,
2648			// 102400). If the EXIF ISO speed rating value is 65535, prefer the XMP
2649			// ISOSpeedRatings tag value.
2650
2651			if (exif.fISOSpeedRatings [j] == 65535)
2652				{
2653
2654				isoSpeedRatingsOptions = preferXMP;
2655
2656				checkXMPForHigherISO = true;
2657
2658				isoSpeedRatingsCount = 0;
2659
2660				break;
2661
2662				}
2663
2664			else if (exif.fISOSpeedRatings [j] == 0)
2665				{
2666				break;
2667				}
2668
2669			isoSpeedRatingsCount++;
2670
2671			}
2672
2673		Sync_uint32_array (XMP_NS_EXIF,
2674						   "ISOSpeedRatings",
2675						   exif.fISOSpeedRatings,
2676						   isoSpeedRatingsCount,
2677						   3,
2678						   isoSpeedRatingsOptions);
2679
2680		// If the EXIF ISO was 65535 and we failed to find anything meaningful in the
2681		// XMP, then we fall back to the EXIF ISO.
2682
2683		if (checkXMPForHigherISO && (isoSpeedRatingsCount == 0))
2684			{
2685
2686			memcpy (exif.fISOSpeedRatings,
2687					oldISOSpeedRatings,
2688					sizeof (oldISOSpeedRatings));
2689
2690			}
2691
2692		// Only remove the ISO tag if there are not ratings over 65535.
2693
2694		if (removeFromXMP)
2695			{
2696
2697			bool hasHighISO = false;
2698
2699			for (uint32 j = 0; j < 3; j++)
2700				{
2701
2702				if (exif.fISOSpeedRatings [j] == 0)
2703					{
2704					break;
2705					}
2706
2707				hasHighISO = hasHighISO || (exif.fISOSpeedRatings [j] > 65535);
2708
2709				}
2710
2711			if (!hasHighISO)
2712				{
2713
2714				Remove (XMP_NS_EXIF, "ISOSpeedRatings");
2715
2716				}
2717
2718			}
2719
2720		}
2721
2722	// SensitivityType:
2723
2724	Sync_uint32 (XMP_NS_EXIF,
2725				 "SensitivityType",
2726				 exif.fSensitivityType,
2727				 exif.fSensitivityType == stUnknown,
2728				 readOnly + removable);
2729
2730	// StandardOutputSensitivity:
2731
2732	Sync_uint32 (XMP_NS_EXIF,
2733				 "StandardOutputSensitivity",
2734				 exif.fStandardOutputSensitivity,
2735				 exif.fStandardOutputSensitivity == 0,
2736				 readOnly + removable);
2737
2738	// RecommendedExposureIndex:
2739
2740	Sync_uint32 (XMP_NS_EXIF,
2741				 "RecommendedExposureIndex",
2742				 exif.fRecommendedExposureIndex,
2743				 exif.fRecommendedExposureIndex == 0,
2744				 readOnly + removable);
2745
2746	// ISOSpeed:
2747
2748	Sync_uint32 (XMP_NS_EXIF,
2749				 "ISOSpeed",
2750				 exif.fISOSpeed,
2751				 exif.fISOSpeed == 0,
2752				 readOnly + removable);
2753
2754	// ISOSpeedLatitudeyyy:
2755
2756	Sync_uint32 (XMP_NS_EXIF,
2757				 "ISOSpeedLatitudeyyy",
2758				 exif.fISOSpeedLatitudeyyy,
2759				 exif.fISOSpeedLatitudeyyy == 0,
2760				 readOnly + removable);
2761
2762	// ISOSpeedLatitudezzz:
2763
2764	Sync_uint32 (XMP_NS_EXIF,
2765				 "ISOSpeedLatitudezzz",
2766				 exif.fISOSpeedLatitudezzz,
2767				 exif.fISOSpeedLatitudezzz == 0,
2768				 readOnly + removable);
2769
2770	// ExposureIndex:
2771
2772	Sync_urational (XMP_NS_EXIF,
2773				    "ExposureIndex",
2774				    exif.fExposureIndex,
2775				    readOnly + removable);
2776
2777	// Brightness Value:
2778
2779	Sync_srational (XMP_NS_EXIF,
2780				    "BrightnessValue",
2781				    exif.fBrightnessValue,
2782				    readOnly + removable);
2783
2784	// Exposure Bias:
2785
2786	Sync_srational (XMP_NS_EXIF,
2787				    "ExposureBiasValue",
2788				    exif.fExposureBiasValue,
2789				    readOnly + removable);
2790
2791	// Max Aperture:
2792
2793	Sync_urational (XMP_NS_EXIF,
2794				    "MaxApertureValue",
2795				    exif.fMaxApertureValue,
2796				    readOnly + removable);
2797
2798	// Subject Distance:
2799
2800	Sync_urational (XMP_NS_EXIF,
2801				    "SubjectDistance",
2802				    exif.fSubjectDistance,
2803				    readOnly + removable);
2804
2805	// Metering Mode:
2806
2807	Sync_uint32 (XMP_NS_EXIF,
2808				 "MeteringMode",
2809				 exif.fMeteringMode,
2810				 exif.fMeteringMode == 0xFFFFFFFF,
2811				 readOnly + removable);
2812
2813	// Light Source:
2814
2815	Sync_uint32 (XMP_NS_EXIF,
2816				 "LightSource",
2817				 exif.fLightSource,
2818				 exif.fLightSource > 0x0FFFF,
2819				 readOnly + removable);
2820
2821	// Flash State:
2822
2823	SyncFlash (exif.fFlash,
2824			   exif.fFlashMask,
2825			   readOnly);
2826
2827	if (removeFromXMP)
2828		{
2829		Remove (XMP_NS_EXIF, "Flash");
2830		}
2831
2832	// Focal Length:
2833
2834	Sync_urational (XMP_NS_EXIF,
2835					"FocalLength",
2836					exif.fFocalLength,
2837					readOnly + removable);
2838
2839	// Sensing Method.
2840
2841	Sync_uint32 (XMP_NS_EXIF,
2842				 "SensingMethod",
2843				 exif.fSensingMethod,
2844				 exif.fSensingMethod > 0x0FFFF,
2845				 readOnly + removable);
2846
2847	// File Source.
2848
2849	Sync_uint32 (XMP_NS_EXIF,
2850				 "FileSource",
2851				 exif.fFileSource,
2852				 exif.fFileSource > 0x0FF,
2853				 readOnly + removable);
2854
2855	// Scene Type.
2856
2857	Sync_uint32 (XMP_NS_EXIF,
2858				 "SceneType",
2859				 exif.fSceneType,
2860				 exif.fSceneType > 0x0FF,
2861				 readOnly + removable);
2862
2863	// Focal Length in 35mm Film:
2864
2865	Sync_uint32 (XMP_NS_EXIF,
2866				 "FocalLengthIn35mmFilm",
2867				 exif.fFocalLengthIn35mmFilm,
2868				 exif.fFocalLengthIn35mmFilm == 0,
2869				 readOnly + removable);
2870
2871	// Custom Rendered:
2872
2873	Sync_uint32 (XMP_NS_EXIF,
2874				 "CustomRendered",
2875				 exif.fCustomRendered,
2876				 exif.fCustomRendered > 0x0FFFF,
2877				 readOnly + removable);
2878
2879	// Exposure Mode:
2880
2881	Sync_uint32 (XMP_NS_EXIF,
2882				 "ExposureMode",
2883				 exif.fExposureMode,
2884				 exif.fExposureMode > 0x0FFFF,
2885				 readOnly + removable);
2886
2887	// White Balance:
2888
2889	Sync_uint32 (XMP_NS_EXIF,
2890				 "WhiteBalance",
2891				 exif.fWhiteBalance,
2892				 exif.fWhiteBalance > 0x0FFFF,
2893				 readOnly + removable);
2894
2895	// Scene Capture Type:
2896
2897	Sync_uint32 (XMP_NS_EXIF,
2898				 "SceneCaptureType",
2899				 exif.fSceneCaptureType,
2900				 exif.fSceneCaptureType > 0x0FFFF,
2901				 readOnly + removable);
2902
2903	// Gain Control:
2904
2905	Sync_uint32 (XMP_NS_EXIF,
2906				 "GainControl",
2907				 exif.fGainControl,
2908				 exif.fGainControl > 0x0FFFF,
2909				 readOnly + removable);
2910
2911	// Contrast:
2912
2913	Sync_uint32 (XMP_NS_EXIF,
2914				 "Contrast",
2915				 exif.fContrast,
2916				 exif.fContrast > 0x0FFFF,
2917				 readOnly + removable);
2918
2919	// Saturation:
2920
2921	Sync_uint32 (XMP_NS_EXIF,
2922				 "Saturation",
2923				 exif.fSaturation,
2924				 exif.fSaturation > 0x0FFFF,
2925				 readOnly + removable);
2926
2927	// Sharpness:
2928
2929	Sync_uint32 (XMP_NS_EXIF,
2930				 "Sharpness",
2931				 exif.fSharpness,
2932				 exif.fSharpness > 0x0FFFF,
2933				 readOnly + removable);
2934
2935	// Subject Distance Range:
2936
2937	Sync_uint32 (XMP_NS_EXIF,
2938				 "SubjectDistanceRange",
2939				 exif.fSubjectDistanceRange,
2940				 exif.fSubjectDistanceRange > 0x0FFFF,
2941				 readOnly + removable);
2942
2943	// Subject Area:
2944
2945	Sync_uint32_array (XMP_NS_EXIF,
2946					   "SubjectArea",
2947					   exif.fSubjectArea,
2948					   exif.fSubjectAreaCount,
2949					   sizeof (exif.fSubjectArea    ) /
2950					   sizeof (exif.fSubjectArea [0]),
2951					   readOnly);
2952
2953	if (removeFromXMP)
2954		{
2955		Remove (XMP_NS_EXIF, "SubjectArea");
2956		}
2957
2958	// Digital Zoom Ratio:
2959
2960	Sync_urational (XMP_NS_EXIF,
2961					"DigitalZoomRatio",
2962					exif.fDigitalZoomRatio,
2963					readOnly + removable);
2964
2965	// Focal Plane Resolution:
2966
2967	Sync_urational (XMP_NS_EXIF,
2968					"FocalPlaneXResolution",
2969					exif.fFocalPlaneXResolution,
2970					readOnly + removable);
2971
2972	Sync_urational (XMP_NS_EXIF,
2973					"FocalPlaneYResolution",
2974					exif.fFocalPlaneYResolution,
2975					readOnly + removable);
2976
2977	Sync_uint32 (XMP_NS_EXIF,
2978				 "FocalPlaneResolutionUnit",
2979				 exif.fFocalPlaneResolutionUnit,
2980				 exif.fFocalPlaneResolutionUnit > 0x0FFFF,
2981				 readOnly + removable);
2982
2983	// ImageDescription:  (XMP is is always preferred)
2984
2985	if (fSDK->GetAltLangDefault (XMP_NS_DC,
2986								 "description",
2987								 exif.fImageDescription))
2988
2989		{
2990
2991		}
2992
2993	else if (doingUpdateFromXMP)
2994		{
2995
2996		exif.fImageDescription.Clear ();
2997
2998		if (originalExif->fImageDescription.NotEmpty ())
2999			{
3000
3001			fSDK->SetAltLangDefault (XMP_NS_DC,
3002									 "description",
3003									 dng_string ());
3004
3005			}
3006
3007		}
3008
3009	else if (exif.fImageDescription.NotEmpty ())
3010		{
3011
3012		fSDK->SetAltLangDefault (XMP_NS_DC,
3013								 "description",
3014								 exif.fImageDescription);
3015
3016		}
3017
3018	// Artist:  (XMP is is always preferred)
3019
3020		{
3021
3022		dng_string_list xmpList;
3023
3024		if (fSDK->GetStringList (XMP_NS_DC,
3025								 "creator",
3026								 xmpList))
3027			{
3028
3029			exif.fArtist.Clear ();
3030
3031			if (xmpList.Count () > 0)
3032				{
3033
3034				uint32 j;
3035
3036				uint32 bufferSize = xmpList.Count () * 4 + 1;
3037
3038				for (j = 0; j < xmpList.Count (); j++)
3039					{
3040
3041					bufferSize += xmpList [j].Length () * 2;
3042
3043					}
3044
3045				dng_memory_data temp (bufferSize);
3046
3047				char *t = temp.Buffer_char ();
3048
3049				for (j = 0; j < xmpList.Count (); j++)
3050					{
3051
3052					const char *s = xmpList [j].Get ();
3053
3054					bool needQuotes = xmpList [j].Contains ("; ") ||
3055									  s [0] == '\"';
3056
3057					if (needQuotes)
3058						{
3059						*(t++) = '\"';
3060						}
3061
3062					while (s [0] != 0)
3063						{
3064
3065						if (s [0] == '\"' && needQuotes)
3066							{
3067							*(t++) = '\"';
3068							}
3069
3070						*(t++) = *(s++);
3071
3072						}
3073
3074					if (needQuotes)
3075						{
3076						*(t++) = '\"';
3077						}
3078
3079					if (j != xmpList.Count () - 1)
3080						{
3081						*(t++) = ';';
3082						*(t++) = ' ';
3083						}
3084					else
3085						{
3086						*t = 0;
3087						}
3088
3089					}
3090
3091				exif.fArtist.Set (temp.Buffer_char ());
3092
3093				}
3094
3095			}
3096
3097		else if (doingUpdateFromXMP)
3098			{
3099
3100			exif.fArtist.Clear ();
3101
3102			if (originalExif->fArtist.NotEmpty ())
3103				{
3104
3105				dng_string_list fakeList;
3106
3107				fakeList.Append (dng_string ());
3108
3109				SetStringList (XMP_NS_DC,
3110							   "creator",
3111							   fakeList,
3112							   false);
3113
3114				}
3115
3116			}
3117
3118		else if (exif.fArtist.NotEmpty ())
3119			{
3120
3121			dng_string_list newList;
3122
3123			dng_memory_data temp (exif.fArtist.Length () + 1);
3124
3125			const char *s = exif.fArtist.Get ();
3126
3127			char *t = temp.Buffer_char ();
3128
3129			bool first = true;
3130
3131			bool quoted = false;
3132
3133			bool valid = true;
3134
3135			while (s [0] != 0 && valid)
3136				{
3137
3138				if (first)
3139					{
3140
3141					if (s [0] == '\"')
3142						{
3143
3144						quoted = true;
3145
3146						s++;
3147
3148						}
3149
3150					}
3151
3152				first = false;
3153
3154				if (quoted)
3155					{
3156
3157					if (s [0] == '\"' &&
3158						s [1] == '\"')
3159						{
3160
3161						s+= 2;
3162
3163						*(t++) = '\"';
3164
3165						}
3166
3167					else if (s [0] == '\"')
3168						{
3169
3170						s++;
3171
3172						quoted = false;
3173
3174						valid = valid && ((s [0] == 0) || ((s [0] == ';' && s [1] == ' ')));
3175
3176						}
3177
3178					else
3179						{
3180
3181						*(t++) = *(s++);
3182
3183						}
3184
3185					}
3186
3187				else if (s [0] == ';' &&
3188						 s [1] == ' ')
3189					{
3190
3191					s += 2;
3192
3193					t [0] = 0;
3194
3195					dng_string ss;
3196
3197					ss.Set (temp.Buffer_char ());
3198
3199					newList.Append (ss);
3200
3201					t = temp.Buffer_char ();
3202
3203					first = true;
3204
3205					}
3206
3207				else
3208					{
3209
3210					*(t++) = *(s++);
3211
3212					}
3213
3214				}
3215
3216			if (quoted)
3217				{
3218
3219				valid = false;
3220
3221				}
3222
3223			if (valid)
3224				{
3225
3226				if (t != temp.Buffer_char ())
3227					{
3228
3229					t [0] = 0;
3230
3231					dng_string ss;
3232
3233					ss.Set (temp.Buffer_char ());
3234
3235					newList.Append (ss);
3236
3237					}
3238
3239				}
3240
3241			else
3242				{
3243
3244				newList.Clear ();
3245
3246				newList.Append (exif.fArtist);
3247
3248				}
3249
3250			SetStringList (XMP_NS_DC,
3251						   "creator",
3252						   newList,
3253						   false);
3254
3255			}
3256
3257		}
3258
3259	// Software:  (XMP is is always preferred)
3260
3261	if (fSDK->GetString (XMP_NS_XAP,
3262						 "CreatorTool",
3263						 exif.fSoftware))
3264
3265		{
3266
3267		}
3268
3269	else if (doingUpdateFromXMP)
3270		{
3271
3272		exif.fSoftware.Clear ();
3273
3274		if (originalExif->fSoftware.NotEmpty ())
3275			{
3276
3277			fSDK->SetString (XMP_NS_XAP,
3278							 "CreatorTool",
3279							 dng_string ());
3280
3281			}
3282
3283		}
3284
3285	else if (exif.fSoftware.NotEmpty ())
3286		{
3287
3288		fSDK->SetString (XMP_NS_XAP,
3289						 "CreatorTool",
3290						 exif.fSoftware);
3291
3292		}
3293
3294	// Copyright:  (XMP is is always preferred)
3295
3296	if (fSDK->GetAltLangDefault (XMP_NS_DC,
3297								 "rights",
3298								 exif.fCopyright))
3299
3300		{
3301
3302		}
3303
3304	else if (doingUpdateFromXMP)
3305		{
3306
3307		exif.fCopyright.Clear ();
3308
3309		if (originalExif->fCopyright.NotEmpty ())
3310			{
3311
3312			fSDK->SetAltLangDefault (XMP_NS_DC,
3313									 "rights",
3314									 dng_string ());
3315
3316			}
3317
3318		}
3319
3320	else if (exif.fCopyright.NotEmpty ())
3321		{
3322
3323		fSDK->SetAltLangDefault (XMP_NS_DC,
3324								 "rights",
3325								 exif.fCopyright);
3326
3327		}
3328
3329	// Camera serial number private tag:
3330
3331	SyncString (XMP_NS_AUX,
3332				"SerialNumber",
3333				exif.fCameraSerialNumber,
3334				readOnly);
3335
3336	// Lens Info:
3337
3338		{
3339
3340		dng_string s;
3341
3342		if (exif.fLensInfo [0].IsValid ())
3343			{
3344
3345			char ss [256];
3346
3347			sprintf (ss,
3348					 "%u/%u %u/%u %u/%u %u/%u",
3349					 (unsigned) exif.fLensInfo [0].n,
3350					 (unsigned) exif.fLensInfo [0].d,
3351					 (unsigned) exif.fLensInfo [1].n,
3352					 (unsigned) exif.fLensInfo [1].d,
3353					 (unsigned) exif.fLensInfo [2].n,
3354					 (unsigned) exif.fLensInfo [2].d,
3355					 (unsigned) exif.fLensInfo [3].n,
3356					 (unsigned) exif.fLensInfo [3].d);
3357
3358			s.Set (ss);
3359
3360			}
3361
3362		SyncString (XMP_NS_AUX,
3363					"LensInfo",
3364				    s,
3365				    readOnly);
3366
3367		if (s.NotEmpty ())
3368			{
3369
3370			unsigned n [4];
3371			unsigned d [4];
3372
3373			if (sscanf (s.Get (),
3374						"%u/%u %u/%u %u/%u %u/%u",
3375						&n [0],
3376						&d [0],
3377						&n [1],
3378						&d [1],
3379						&n [2],
3380						&d [2],
3381						&n [3],
3382						&d [3]) == 8)
3383				{
3384
3385				for (uint32 j = 0; j < 4; j++)
3386					{
3387
3388					exif.fLensInfo [j] = dng_urational (n [j], d [j]);
3389
3390					}
3391
3392				}
3393
3394
3395			}
3396
3397		}
3398
3399	// Lens name:
3400
3401		{
3402
3403		// EXIF lens names are sometimes missing or wrong (esp. when non-OEM lenses
3404		// are used). So prefer the value from XMP.
3405
3406		SyncString (XMP_NS_AUX,
3407					"Lens",
3408					exif.fLensName,
3409					preferXMP);
3410
3411		// Generate default lens name from lens info if required.
3412		// Ignore names names that end in "f/0.0" due to third party bug.
3413
3414		if ((exif.fLensName.IsEmpty () ||
3415			 exif.fLensName.EndsWith ("f/0.0")) && exif.fLensInfo [0].IsValid ())
3416			{
3417
3418			char s [256];
3419
3420			real64 minFL = exif.fLensInfo [0].As_real64 ();
3421			real64 maxFL = exif.fLensInfo [1].As_real64 ();
3422
3423			// The f-stop numbers are optional.
3424
3425			if (exif.fLensInfo [2].IsValid ())
3426				{
3427
3428				real64 minFS = exif.fLensInfo [2].As_real64 ();
3429				real64 maxFS = exif.fLensInfo [3].As_real64 ();
3430
3431				if (minFL == maxFL)
3432					sprintf (s, "%.1f mm f/%.1f", minFL, minFS);
3433
3434				else if (minFS == maxFS)
3435					sprintf (s, "%.1f-%.1f mm f/%.1f", minFL, maxFL, minFS);
3436
3437				else
3438					sprintf (s, "%.1f-%.1f mm f/%.1f-%.1f", minFL, maxFL, minFS, maxFS);
3439
3440				}
3441
3442			else
3443				{
3444
3445				if (minFL == maxFL)
3446					sprintf (s, "%.1f mm", minFL);
3447
3448				else
3449					sprintf (s, "%.1f-%.1f mm", minFL, maxFL);
3450
3451				}
3452
3453			exif.fLensName.Set (s);
3454
3455			SetString (XMP_NS_AUX,
3456					   "Lens",
3457					   exif.fLensName);
3458
3459			}
3460
3461		}
3462
3463	// Lens ID:
3464
3465	SyncString (XMP_NS_AUX,
3466				"LensID",
3467				exif.fLensID,
3468				readOnly);
3469
3470	// Lens Make:
3471
3472	SyncString (XMP_NS_EXIF,
3473				"LensMake",
3474				exif.fLensMake,
3475				readOnly + removable);
3476
3477	// Lens Serial Number:
3478
3479	SyncString (XMP_NS_AUX,
3480				"LensSerialNumber",
3481				exif.fLensSerialNumber,
3482				readOnly);
3483
3484	// Image Number:
3485
3486	Sync_uint32 (XMP_NS_AUX,
3487				 "ImageNumber",
3488				 exif.fImageNumber,
3489				 exif.fImageNumber == 0xFFFFFFFF,
3490				 readOnly);
3491
3492	// User Comment:
3493
3494	if (exif.fUserComment.NotEmpty ())
3495		{
3496
3497		fSDK->SetAltLangDefault (XMP_NS_EXIF,
3498								 "UserComment",
3499								 exif.fUserComment);
3500
3501		}
3502
3503	else
3504		{
3505
3506		(void) fSDK->GetAltLangDefault (XMP_NS_EXIF,
3507									  	"UserComment",
3508									  	exif.fUserComment);
3509
3510		}
3511
3512	if (removeFromXMP)
3513		{
3514		Remove (XMP_NS_EXIF, "UserComment");
3515		}
3516
3517	// Approximate focus distance:
3518
3519	SyncApproximateFocusDistance (exif,
3520								  readOnly);
3521
3522	// Flash Compensation:
3523
3524	Sync_srational (XMP_NS_AUX,
3525				    "FlashCompensation",
3526				    exif.fFlashCompensation,
3527				    readOnly);
3528
3529	// Owner Name: (allow XMP updates)
3530
3531	SyncString (XMP_NS_AUX,
3532				"OwnerName",
3533				exif.fOwnerName,
3534				preferXMP);
3535
3536	// Firmware:
3537
3538	SyncString (XMP_NS_AUX,
3539				"Firmware",
3540				exif.fFirmware,
3541				readOnly);
3542
3543	// Image Unique ID:
3544
3545		{
3546
3547		dng_string s = EncodeFingerprint (exif.fImageUniqueID);
3548
3549		SyncString (XMP_NS_EXIF,
3550				    "ImageUniqueID",
3551				    s,
3552				    readOnly + removable);
3553
3554		exif.fImageUniqueID = DecodeFingerprint (s);
3555
3556		}
3557
3558	// Allow EXIF GPS to be updated via updates from XMP.
3559
3560	if (doingUpdateFromXMP)
3561		{
3562
3563		// Require that at least one basic GPS field exist in the
3564		// XMP before overrriding the EXIF GPS fields.
3565
3566		if (Exists (XMP_NS_EXIF, "GPSVersionID"       ) ||
3567			Exists (XMP_NS_EXIF, "GPSLatitude"        ) ||
3568			Exists (XMP_NS_EXIF, "GPSLongitude"       ) ||
3569			Exists (XMP_NS_EXIF, "GPSAltitude"        ) ||
3570			Exists (XMP_NS_EXIF, "GPSTimeStamp"       ) ||
3571			Exists (XMP_NS_EXIF, "GPSProcessingMethod"))
3572			{
3573
3574			// Clear out the GPS info from the EXIF so it will
3575			// replaced by the GPS info from the XMP.
3576
3577			dng_exif blankExif;
3578
3579			exif.CopyGPSFrom (blankExif);
3580
3581			}
3582
3583		}
3584
3585	// GPS Version ID:
3586
3587		{
3588
3589		dng_string s = EncodeGPSVersion (exif.fGPSVersionID);
3590
3591		if (SyncString (XMP_NS_EXIF,
3592						"GPSVersionID",
3593						s,
3594						preferNonXMP + removable))
3595			{
3596
3597			exif.fGPSVersionID = DecodeGPSVersion (s);
3598
3599			}
3600
3601		}
3602
3603	// GPS Latitude:
3604
3605		{
3606
3607		dng_string s = EncodeGPSCoordinate (exif.fGPSLatitudeRef,
3608					  						exif.fGPSLatitude);
3609
3610		if (SyncString (XMP_NS_EXIF,
3611						"GPSLatitude",
3612						s,
3613						preferNonXMP + removable))
3614			{
3615
3616			DecodeGPSCoordinate (s,
3617								 exif.fGPSLatitudeRef,
3618								 exif.fGPSLatitude);
3619
3620			}
3621
3622		}
3623
3624	// GPS Longitude:
3625
3626		{
3627
3628		dng_string s = EncodeGPSCoordinate (exif.fGPSLongitudeRef,
3629					  						exif.fGPSLongitude);
3630
3631		if (SyncString (XMP_NS_EXIF,
3632						"GPSLongitude",
3633						s,
3634						preferNonXMP + removable))
3635			{
3636
3637			DecodeGPSCoordinate (s,
3638								 exif.fGPSLongitudeRef,
3639								 exif.fGPSLongitude);
3640
3641			}
3642
3643		}
3644
3645	// Handle simple case of incorrectly written GPS altitude where someone didn't understand the GPSAltitudeRef and assumed the GPSAltitude RATIONAL is signed.
3646	// Only handle this case as we do not want to misinterpret e.g. a fixed point representation of very high GPS altitudes.
3647
3648	uint32 &altitudeRef = exif.fGPSAltitudeRef;
3649	dng_urational &altitude = exif.fGPSAltitude;
3650
3651	if (altitude.IsValid () &&
3652		(altitudeRef == 0 || altitudeRef == 0xFFFFFFFF))  // If the file contains a "below sea level" altitudeRef, assume the writing software is working according to the spec.
3653		{
3654
3655		if ((altitude.n & (1U << 31)) &&
3656			altitude.d < 7) // As the denominator increases, large numerator values become possibly valid distances. Pick a limit on the conservative side (approx 33e6m) to prevent misinterpretation.
3657							// Noting that the normal case for this mistake has a denominator of 1
3658			{
3659
3660			altitude.n = ~altitude.n + 1;
3661			altitudeRef = 1;
3662
3663			}
3664
3665		}
3666
3667	// GPS Altitude Reference:
3668
3669	Sync_uint32 (XMP_NS_EXIF,
3670				 "GPSAltitudeRef",
3671				 altitudeRef,
3672				 altitudeRef == 0xFFFFFFFF,
3673				 preferNonXMP + removable);
3674
3675	// GPS Altitude:
3676
3677	Sync_urational (XMP_NS_EXIF,
3678					"GPSAltitude",
3679					altitude,
3680					preferNonXMP + removable);
3681
3682	// GPS Date/Time:
3683
3684		{
3685
3686		dng_string s = EncodeGPSDateTime (exif.fGPSDateStamp,
3687										  exif.fGPSTimeStamp);
3688
3689		if (SyncString (XMP_NS_EXIF,
3690						"GPSTimeStamp",
3691						s,
3692						preferNonXMP + removable))
3693			{
3694
3695			DecodeGPSDateTime (s,
3696							   exif.fGPSDateStamp,
3697							   exif.fGPSTimeStamp);
3698
3699			}
3700
3701		}
3702
3703	// GPS Satellites:
3704
3705	SyncString (XMP_NS_EXIF,
3706				"GPSSatellites",
3707				exif.fGPSSatellites,
3708				preferNonXMP + removable);
3709
3710	// GPS Status:
3711
3712	SyncString (XMP_NS_EXIF,
3713				"GPSStatus",
3714				exif.fGPSStatus,
3715				preferNonXMP + removable);
3716
3717	// GPS Measure Mode:
3718
3719	SyncString (XMP_NS_EXIF,
3720				"GPSMeasureMode",
3721				exif.fGPSMeasureMode,
3722				preferNonXMP + removable);
3723
3724	// GPS DOP:
3725
3726	Sync_urational (XMP_NS_EXIF,
3727					"GPSDOP",
3728					exif.fGPSDOP,
3729					preferNonXMP + removable);
3730
3731	// GPS Speed Reference:
3732
3733	SyncString (XMP_NS_EXIF,
3734				"GPSSpeedRef",
3735				exif.fGPSSpeedRef,
3736				preferNonXMP + removable);
3737
3738	// GPS Speed:
3739
3740	Sync_urational (XMP_NS_EXIF,
3741					"GPSSpeed",
3742					exif.fGPSSpeed,
3743					preferNonXMP + removable);
3744
3745	// GPS Track Reference:
3746
3747	SyncString (XMP_NS_EXIF,
3748				"GPSTrackRef",
3749				exif.fGPSTrackRef,
3750				preferNonXMP + removable);
3751
3752	// GPS Track:
3753
3754	Sync_urational (XMP_NS_EXIF,
3755					"GPSTrack",
3756					exif.fGPSTrack,
3757					preferNonXMP + removable);
3758
3759	// GPS Image Direction Reference:
3760
3761	SyncString (XMP_NS_EXIF,
3762				"GPSImgDirectionRef",
3763				exif.fGPSImgDirectionRef,
3764				preferNonXMP + removable);
3765
3766	// GPS Image Direction:
3767
3768	Sync_urational (XMP_NS_EXIF,
3769					"GPSImgDirection",
3770					exif.fGPSImgDirection,
3771					preferNonXMP + removable);
3772
3773	// GPS Map Datum:
3774
3775	SyncString (XMP_NS_EXIF,
3776				"GPSMapDatum",
3777				exif.fGPSMapDatum,
3778				preferNonXMP + removable);
3779
3780	// GPS Destination Latitude:
3781
3782		{
3783
3784		dng_string s = EncodeGPSCoordinate (exif.fGPSDestLatitudeRef,
3785					  						exif.fGPSDestLatitude);
3786
3787		if (SyncString (XMP_NS_EXIF,
3788						"GPSDestLatitude",
3789						s,
3790						preferNonXMP + removable))
3791			{
3792
3793			DecodeGPSCoordinate (s,
3794								 exif.fGPSDestLatitudeRef,
3795								 exif.fGPSDestLatitude);
3796
3797			}
3798
3799		}
3800
3801	// GPS Destination Longitude:
3802
3803		{
3804
3805		dng_string s = EncodeGPSCoordinate (exif.fGPSDestLongitudeRef,
3806					  						exif.fGPSDestLongitude);
3807
3808		if (SyncString (XMP_NS_EXIF,
3809						"GPSDestLongitude",
3810						s,
3811						preferNonXMP + removable))
3812			{
3813
3814			DecodeGPSCoordinate (s,
3815								 exif.fGPSDestLongitudeRef,
3816								 exif.fGPSDestLongitude);
3817
3818			}
3819
3820		}
3821
3822	// GPS Destination Bearing Reference:
3823
3824	SyncString (XMP_NS_EXIF,
3825				"GPSDestBearingRef",
3826				exif.fGPSDestBearingRef,
3827				preferNonXMP + removable);
3828
3829	// GPS Destination Bearing:
3830
3831	Sync_urational (XMP_NS_EXIF,
3832					"GPSDestBearing",
3833					exif.fGPSDestBearing,
3834					preferNonXMP + removable);
3835
3836	// GPS Destination Distance Reference:
3837
3838	SyncString (XMP_NS_EXIF,
3839				"GPSDestDistanceRef",
3840				exif.fGPSDestDistanceRef,
3841				preferNonXMP + removable);
3842
3843	// GPS Destination Distance:
3844
3845	Sync_urational (XMP_NS_EXIF,
3846					"GPSDestDistance",
3847					exif.fGPSDestDistance,
3848					preferNonXMP + removable);
3849
3850	// GPS Processing Method:
3851
3852	SyncString (XMP_NS_EXIF,
3853				"GPSProcessingMethod",
3854				exif.fGPSProcessingMethod,
3855				preferNonXMP + removable);
3856
3857	// GPS Area Information:
3858
3859	SyncString (XMP_NS_EXIF,
3860				"GPSAreaInformation",
3861				exif.fGPSAreaInformation,
3862				preferNonXMP + removable);
3863
3864	// GPS Differential:
3865
3866	Sync_uint32 (XMP_NS_EXIF,
3867				 "GPSDifferential",
3868				 exif.fGPSDifferential,
3869				 exif.fGPSDifferential == 0xFFFFFFFF,
3870				 preferNonXMP + removable);
3871
3872	// GPS Horizontal Positioning Error:
3873
3874	Sync_urational (XMP_NS_EXIF,
3875					"GPSHPositioningError",
3876					exif.fGPSHPositioningError,
3877					preferNonXMP + removable);
3878
3879	// Sync date/times.
3880
3881	UpdateExifDates (exif, removeFromXMP);
3882
3883	// We are syncing EXIF and XMP, but we are not updating the
3884	// NativeDigest tags.  It is better to just delete them than leave
3885	// the stale values around.
3886
3887	Remove (XMP_NS_EXIF, "NativeDigest");
3888	Remove (XMP_NS_TIFF, "NativeDigest");
3889
3890	}
3891
3892/*****************************************************************************/
3893
3894void dng_xmp::SyncApproximateFocusDistance (dng_exif &exif,
3895											const uint32 readOnly)
3896	{
3897
3898	Sync_urational (XMP_NS_AUX,
3899					"ApproximateFocusDistance",
3900					exif.fApproxFocusDistance,
3901					readOnly);
3902
3903	}
3904
3905/******************************************************************************/
3906
3907void dng_xmp::ValidateStringList (const char *ns,
3908							      const char *path)
3909	{
3910
3911	fSDK->ValidateStringList (ns, path);
3912
3913	}
3914
3915/******************************************************************************/
3916
3917void dng_xmp::ValidateMetadata ()
3918	{
3919
3920	// The following values should be arrays, but are not always.  So
3921	// fix them up because Photoshop sometimes has problems parsing invalid
3922	// tags.
3923
3924	ValidateStringList (XMP_NS_DC, "creator");
3925
3926	ValidateStringList (XMP_NS_PHOTOSHOP, "Keywords");
3927	ValidateStringList (XMP_NS_PHOTOSHOP, "SupplementalCategories");
3928
3929	}
3930
3931/******************************************************************************/
3932
3933bool dng_xmp::DateTimeIsDateOnly (const char *ns,
3934							      const char *path)
3935	{
3936
3937	dng_string s;
3938
3939	if (GetString (ns, path, s))
3940		{
3941
3942		uint32 len = s.Length ();
3943
3944		if (len)
3945			{
3946
3947			for (uint32 j = 0; j < len; j++)
3948				{
3949
3950				if (s.Get () [j] == 'T')
3951					{
3952
3953					return false;
3954
3955					}
3956
3957				}
3958
3959			return true;
3960
3961			}
3962
3963		}
3964
3965	return false;
3966
3967	}
3968
3969/******************************************************************************/
3970
3971void dng_xmp::UpdateExifDates (dng_exif &exif,
3972							   bool removeFromXMP)
3973	{
3974
3975	// For the following three date/time fields, we always prefer XMP to
3976	// the EXIF values.  This is to allow the user to correct the date/times
3977	// via changes in a sidecar XMP file, without modifying the original
3978	// raw file.
3979
3980	// Kludge: The Nikon D4 is writing date only date/times into XMP, so
3981	// prefer the EXIF values if the XMP only contains a date.
3982
3983	// Modification Date/Time:
3984	// exif.fDateTime
3985	// kXMP_NS_XMP:"ModifyDate" & kXMP_NS_TIFF:"DateTime" are aliased
3986
3987		{
3988
3989		dng_string s = exif.fDateTime.Encode_ISO_8601 ();
3990
3991		bool dateOnly = DateTimeIsDateOnly (XMP_NS_TIFF, "DateTime");
3992
3993		SyncString (XMP_NS_TIFF,
3994					"DateTime",
3995					s,
3996					dateOnly ? preferNonXMP : preferXMP);
3997
3998		if (s.NotEmpty ())
3999			{
4000
4001			exif.fDateTime.Decode_ISO_8601 (s.Get ());
4002
4003			// Round trip again in case we need to add a fake time zone.
4004
4005			s = exif.fDateTime.Encode_ISO_8601 ();
4006
4007			SetString (XMP_NS_TIFF,
4008					   "DateTime",
4009					   s);
4010
4011			}
4012
4013		}
4014
4015	// Original Date/Time:
4016	// exif.fDateTimeOriginal
4017	// IPTC: DateCreated
4018	// XMP_NS_EXIF:"DateTimeOriginal" & XMP_NS_PHOTOSHOP:"DateCreated"
4019	// Adobe has decided to keep the two XMP fields separate.
4020
4021		{
4022
4023		dng_string s = exif.fDateTimeOriginal.Encode_ISO_8601 ();
4024
4025		bool dateOnly = DateTimeIsDateOnly (XMP_NS_EXIF, "DateTimeOriginal");
4026
4027		SyncString (XMP_NS_EXIF,
4028					"DateTimeOriginal",
4029					s,
4030					dateOnly ? preferNonXMP : preferXMP);
4031
4032		if (s.NotEmpty ())
4033			{
4034
4035			exif.fDateTimeOriginal.Decode_ISO_8601 (s.Get ());
4036
4037			// Round trip again in case we need to add a fake time zone.
4038
4039			s = exif.fDateTimeOriginal.Encode_ISO_8601 ();
4040
4041			SetString (XMP_NS_EXIF,
4042					   "DateTimeOriginal",
4043					   s);
4044
4045			}
4046
4047		// Sync the IPTC value to the EXIF value if only the EXIF
4048		// value exists.
4049
4050		if (s.NotEmpty () && !Exists (XMP_NS_PHOTOSHOP, "DateCreated"))
4051			{
4052
4053			SetString (XMP_NS_PHOTOSHOP, "DateCreated", s);
4054
4055			}
4056
4057		if (removeFromXMP)
4058			{
4059
4060			Remove (XMP_NS_EXIF, "DateTimeOriginal");
4061
4062			}
4063
4064		}
4065
4066	// Date Time Digitized:
4067	// XMP_NS_EXIF:"DateTimeDigitized" & kXMP_NS_XMP:"CreateDate" are aliased
4068
4069		{
4070
4071		dng_string s = exif.fDateTimeDigitized.Encode_ISO_8601 ();
4072
4073		bool dateOnly = DateTimeIsDateOnly (XMP_NS_EXIF, "DateTimeDigitized");
4074
4075		SyncString (XMP_NS_EXIF,
4076					"DateTimeDigitized",
4077					s,
4078					dateOnly ? preferNonXMP : preferXMP);
4079
4080		if (s.NotEmpty ())
4081			{
4082
4083			exif.fDateTimeDigitized.Decode_ISO_8601 (s.Get ());
4084
4085			// Round trip again in case we need to add a fake time zone.
4086
4087			s = exif.fDateTimeDigitized.Encode_ISO_8601 ();
4088
4089			SetString (XMP_NS_EXIF,
4090					   "DateTimeDigitized",
4091					   s);
4092
4093			}
4094
4095		}
4096
4097	}
4098
4099/******************************************************************************/
4100
4101void dng_xmp::UpdateDateTime (const dng_date_time_info &dt)
4102	{
4103
4104	dng_string s = dt.Encode_ISO_8601 ();
4105
4106	SetString (XMP_NS_TIFF,
4107			   "DateTime",
4108			   s);
4109
4110	}
4111
4112/******************************************************************************/
4113
4114void dng_xmp::UpdateMetadataDate (const dng_date_time_info &dt)
4115	{
4116
4117	dng_string s = dt.Encode_ISO_8601 ();
4118
4119	SetString (XMP_NS_XAP,
4120			   "MetadataDate",
4121			   s);
4122
4123	}
4124
4125/*****************************************************************************/
4126
4127bool dng_xmp::HasOrientation () const
4128	{
4129
4130	uint32 x = 0;
4131
4132	if (Get_uint32 (XMP_NS_TIFF,
4133					"Orientation",
4134					x))
4135		{
4136
4137		return (x >= 1) && (x <= 8);
4138
4139		}
4140
4141	return false;
4142
4143	}
4144
4145/*****************************************************************************/
4146
4147dng_orientation dng_xmp::GetOrientation () const
4148	{
4149
4150	dng_orientation result;
4151
4152	uint32 x = 0;
4153
4154	if (Get_uint32 (XMP_NS_TIFF,
4155					"Orientation",
4156					x))
4157		{
4158
4159		if ((x >= 1) && (x <= 8))
4160			{
4161
4162			result.SetTIFF (x);
4163
4164			}
4165
4166		}
4167
4168	return result;
4169
4170	}
4171
4172/******************************************************************************/
4173
4174void dng_xmp::ClearOrientation ()
4175	{
4176
4177	fSDK->Remove (XMP_NS_TIFF, "Orientation");
4178
4179	}
4180
4181/******************************************************************************/
4182
4183void dng_xmp::SetOrientation (const dng_orientation &orientation)
4184	{
4185
4186	Set_uint32 (XMP_NS_TIFF,
4187			    "Orientation",
4188				orientation.GetTIFF ());
4189
4190	}
4191
4192/*****************************************************************************/
4193
4194void dng_xmp::SyncOrientation (dng_negative &negative,
4195					   		   bool xmpIsMaster)
4196	{
4197
4198	SyncOrientation (negative.Metadata (), xmpIsMaster);
4199
4200	}
4201
4202/*****************************************************************************/
4203
4204void dng_xmp::SyncOrientation (dng_metadata &metadata,
4205					   		   bool xmpIsMaster)
4206	{
4207
4208	// See if XMP contains the orientation.
4209
4210	bool xmpHasOrientation = HasOrientation ();
4211
4212	// See if XMP is the master value.
4213
4214	if (xmpHasOrientation && (xmpIsMaster || !metadata.HasBaseOrientation ()))
4215		{
4216
4217		metadata.SetBaseOrientation (GetOrientation ());
4218
4219		}
4220
4221	else
4222		{
4223
4224		SetOrientation (metadata.BaseOrientation ());
4225
4226		}
4227
4228	}
4229
4230/******************************************************************************/
4231
4232void dng_xmp::ClearImageInfo ()
4233	{
4234
4235	Remove (XMP_NS_TIFF, "ImageWidth" );
4236	Remove (XMP_NS_TIFF, "ImageLength");
4237
4238	Remove (XMP_NS_EXIF, "PixelXDimension");
4239	Remove (XMP_NS_EXIF, "PixelYDimension");
4240
4241	Remove (XMP_NS_TIFF, "BitsPerSample");
4242
4243	Remove (XMP_NS_TIFF, "Compression");
4244
4245	Remove (XMP_NS_TIFF, "PhotometricInterpretation");
4246
4247	// "Orientation" is handled separately.
4248
4249	Remove (XMP_NS_TIFF, "SamplesPerPixel");
4250
4251	Remove (XMP_NS_TIFF, "PlanarConfiguration");
4252
4253	Remove (XMP_NS_TIFF, "XResolution");
4254	Remove (XMP_NS_TIFF, "YResolution");
4255
4256	Remove (XMP_NS_TIFF, "ResolutionUnit");
4257
4258	Remove (XMP_NS_PHOTOSHOP, "ColorMode" );
4259	Remove (XMP_NS_PHOTOSHOP, "ICCProfile");
4260
4261	}
4262
4263/******************************************************************************/
4264
4265void dng_xmp::SetImageSize (const dng_point &size)
4266	{
4267
4268	Set_uint32 (XMP_NS_TIFF, "ImageWidth" , size.h);
4269	Set_uint32 (XMP_NS_TIFF, "ImageLength", size.v);
4270
4271	// Mirror these values to the EXIF tags.
4272
4273	Set_uint32 (XMP_NS_EXIF, "PixelXDimension" , size.h);
4274	Set_uint32 (XMP_NS_EXIF, "PixelYDimension" , size.v);
4275
4276	}
4277
4278/******************************************************************************/
4279
4280void dng_xmp::SetSampleInfo (uint32 samplesPerPixel,
4281							 uint32 bitsPerSample)
4282	{
4283
4284	Set_uint32 (XMP_NS_TIFF, "SamplesPerPixel", samplesPerPixel);
4285
4286	char s [32];
4287
4288	sprintf (s, "%u", (unsigned) bitsPerSample);
4289
4290	dng_string ss;
4291
4292	ss.Set (s);
4293
4294	dng_string_list list;
4295
4296	for (uint32 j = 0; j < samplesPerPixel; j++)
4297		{
4298		list.Append (ss);
4299		}
4300
4301	SetStringList (XMP_NS_TIFF, "BitsPerSample", list, false);
4302
4303	}
4304
4305/******************************************************************************/
4306
4307void dng_xmp::SetPhotometricInterpretation (uint32 pi)
4308	{
4309
4310	Set_uint32 (XMP_NS_TIFF, "PhotometricInterpretation", pi);
4311
4312	}
4313
4314/******************************************************************************/
4315
4316void dng_xmp::SetResolution (const dng_resolution &res)
4317	{
4318
4319 	Set_urational (XMP_NS_TIFF, "XResolution", res.fXResolution);
4320	Set_urational (XMP_NS_TIFF, "YResolution", res.fYResolution);
4321
4322    Set_uint32 (XMP_NS_TIFF, "ResolutionUnit", res.fResolutionUnit);
4323
4324	}
4325
4326/*****************************************************************************/
4327
4328void dng_xmp::ComposeArrayItemPath (const char *ns,
4329									const char *arrayName,
4330									int32 itemNumber,
4331									dng_string &s) const
4332	{
4333
4334	fSDK->ComposeArrayItemPath (ns, arrayName, itemNumber, s);
4335
4336	}
4337
4338/*****************************************************************************/
4339
4340void dng_xmp::ComposeStructFieldPath (const char *ns,
4341									  const char *structName,
4342									  const char *fieldNS,
4343									  const char *fieldName,
4344									  dng_string &s) const
4345	{
4346
4347	fSDK->ComposeStructFieldPath (ns, structName, fieldNS, fieldName, s);
4348
4349	}
4350
4351/*****************************************************************************/
4352
4353int32 dng_xmp::CountArrayItems (const char *ns,
4354							    const char *path) const
4355	{
4356
4357	return fSDK->CountArrayItems (ns, path);
4358
4359	}
4360
4361/*****************************************************************************/
4362
4363void dng_xmp::AppendArrayItem (const char *ns,
4364							   const char *arrayName,
4365							   const char *itemValue,
4366							   bool isBag,
4367							   bool propIsStruct)
4368	{
4369
4370	fSDK->AppendArrayItem (ns,
4371						   arrayName,
4372						   itemValue,
4373						   isBag,
4374						   propIsStruct);
4375	}
4376
4377/*****************************************************************************/
4378
4379#if qDNGXMPDocOps
4380
4381/*****************************************************************************/
4382
4383void dng_xmp::DocOpsOpenXMP (const char *srcMIMI)
4384	{
4385
4386	fSDK->DocOpsOpenXMP (srcMIMI);
4387
4388	}
4389
4390/*****************************************************************************/
4391
4392void dng_xmp::DocOpsPrepareForSave (const char *srcMIMI,
4393									const char *dstMIMI,
4394									bool newPath)
4395	{
4396
4397	fSDK->DocOpsPrepareForSave (srcMIMI,
4398								dstMIMI,
4399								newPath);
4400
4401	}
4402
4403/*****************************************************************************/
4404
4405void dng_xmp::DocOpsUpdateMetadata (const char *srcMIMI)
4406	{
4407
4408	fSDK->DocOpsUpdateMetadata (srcMIMI);
4409
4410	}
4411
4412/*****************************************************************************/
4413
4414#endif
4415
4416#endif
4417/*****************************************************************************/
4418