• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
dng_xmp(dng_memory_allocator & allocator)33 dng_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 
dng_xmp(const dng_xmp & xmp)52 dng_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 
~dng_xmp()71 dng_xmp::~dng_xmp ()
72 	{
73 
74 	if (fSDK)
75 		{
76 
77 		delete fSDK;
78 
79 		}
80 
81 	}
82 
83 /*****************************************************************************/
84 
Clone() const85 dng_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 
TrimDecimal(char * s)101 void 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 
EncodeFingerprint(const dng_fingerprint & f,bool allowInvalid)129 dng_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 
DecodeFingerprint(const dng_string & s)152 dng_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 
EncodeGPSVersion(uint32 version)166 dng_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 
DecodeGPSVersion(const dng_string & s)203 uint32 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 
EncodeGPSCoordinate(const dng_string & ref,const dng_urational * coord)239 dng_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 
DecodeGPSCoordinate(const dng_string & s,dng_string & ref,dng_urational * coord)322 void 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 
EncodeGPSDateTime(const dng_string & dateStamp,const dng_urational * timeStamp)401 dng_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 
DecodeGPSDateTime(const dng_string & s,dng_string & dateStamp,dng_urational * timeStamp)474 void 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 
Parse(dng_host & host,const void * buffer,uint32 count)547 void 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 
Serialize(bool asPacket,uint32 targetBytes,uint32 padBytes,bool forJPEG,bool compact) const560 dng_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 
PackageForJPEG(AutoPtr<dng_memory_block> & stdBlock,AutoPtr<dng_memory_block> & extBlock,dng_string & extDigest) const578 void 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 
MergeFromJPEG(const dng_xmp & xmp)592 void dng_xmp::MergeFromJPEG (const dng_xmp &xmp)
593 	{
594 
595 	fSDK->MergeFromJPEG (xmp.fSDK);
596 
597 	}
598 
599 /*****************************************************************************/
600 
HasMeta() const601 bool dng_xmp::HasMeta () const
602 	{
603 
604 	return fSDK->HasMeta ();
605 
606 	}
607 
608 /*****************************************************************************/
609 
GetPrivateMeta()610 void * dng_xmp::GetPrivateMeta ()
611 	{
612 
613 	return fSDK->GetPrivateMeta ();
614 
615 	}
616 
617 /*****************************************************************************/
618 
Exists(const char * ns,const char * path) const619 bool dng_xmp::Exists (const char *ns,
620 					  const char *path) const
621 	{
622 
623 	return fSDK->Exists (ns, path);
624 
625 	}
626 
627 /*****************************************************************************/
628 
HasNameSpace(const char * ns) const629 bool dng_xmp::HasNameSpace (const char *ns) const
630 	{
631 
632 	return fSDK->HasNameSpace (ns);
633 
634 	}
635 
636 /*****************************************************************************/
637 
IteratePaths(IteratePathsCallback * callback,void * callbackData,const char * ns,const char * path)638 bool 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 
Remove(const char * ns,const char * path)650 void dng_xmp::Remove (const char *ns,
651 				      const char *path)
652 	{
653 
654 	fSDK->Remove (ns, path);
655 
656 	}
657 
658 /*****************************************************************************/
659 
RemoveProperties(const char * ns)660 void dng_xmp::RemoveProperties (const char *ns)
661 	{
662 
663 	fSDK->RemoveProperties (ns);
664 
665 	}
666 
667 /*****************************************************************************/
668 
RemoveEmptyStringOrArray(const char * ns,const char * path)669 void 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 
RemoveEmptyStringsAndArraysCallback(const char * ns,const char * path,void * callbackData)690 static 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 
RemoveEmptyStringsAndArrays(const char * ns)705 void dng_xmp::RemoveEmptyStringsAndArrays (const char *ns)
706 	{
707 
708 	IteratePaths (RemoveEmptyStringsAndArraysCallback,
709 				  (void *) this,
710 				  ns,
711 				  NULL);
712 
713 	}
714 
715 /*****************************************************************************/
716 
Set(const char * ns,const char * path,const char * text)717 void 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 
GetString(const char * ns,const char * path,dng_string & s) const728 bool 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 
SetString(const char * ns,const char * path,const dng_string & s)739 void 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 
SyncString(const char * ns,const char * path,dng_string & s,uint32 options)750 bool 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 
GetStringList(const char * ns,const char * path,dng_string_list & list) const847 bool 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 
SetStringList(const char * ns,const char * path,const dng_string_list & list,bool isBag)858 void 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 
SyncStringList(const char * ns,const char * path,dng_string_list & list,bool isBag,uint32 options)870 void 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 
SetStructField(const char * ns,const char * path,const char * fieldNS,const char * fieldName,const dng_string & s)945 void 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 
SetStructField(const char * ns,const char * path,const char * fieldNS,const char * fieldName,const char * s)964 void 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 
DeleteStructField(const char * ns,const char * path,const char * fieldNS,const char * fieldName)977 void 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 
GetStructField(const char * ns,const char * path,const char * fieldNS,const char * fieldName,dng_string & s) const989 bool 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 
SetAltLangDefault(const char * ns,const char * path,const dng_string & s)1002 void 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 
GetAltLangDefault(const char * ns,const char * path,dng_string & s) const1013 bool 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 
SyncAltLangDefault(const char * ns,const char * path,dng_string & s,uint32 options)1024 bool 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 
GetBoolean(const char * ns,const char * path,bool & x) const1095 bool 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 
SetBoolean(const char * ns,const char * path,bool x)1131 void 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 
Get_int32(const char * ns,const char * path,int32 & x) const1142 bool 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 
Set_int32(const char * ns,const char * path,int32 x,bool usePlus)1176 void 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 
Get_uint32(const char * ns,const char * path,uint32 & x) const1199 bool 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 
Set_uint32(const char * ns,const char * path,uint32 x)1233 void 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 
Sync_uint32(const char * ns,const char * path,uint32 & x,bool isDefault,uint32 options)1250 void 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 
Sync_uint32_array(const char * ns,const char * path,uint32 * data,uint32 & count,uint32 maxCount,uint32 options)1344 void 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 
Get_real64(const char * ns,const char * path,real64 & x) const1402 bool 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 
Set_real64(const char * ns,const char * path,real64 x,uint32 places,bool trim,bool usePlus)1436 void 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 
Get_urational(const char * ns,const char * path,dng_urational & r) const1476 bool 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 
Set_urational(const char * ns,const char * path,const dng_urational & r)1516 void 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 
Sync_urational(const char * ns,const char * path,dng_urational & r,uint32 options)1534 void 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 
Get_srational(const char * ns,const char * path,dng_srational & r) const1629 bool 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 
Set_srational(const char * ns,const char * path,const dng_srational & r)1669 void 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 
Sync_srational(const char * ns,const char * path,dng_srational & r,uint32 options)1687 void 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 
GetFingerprint(const char * ns,const char * path,dng_fingerprint & print) const1782 bool 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 
SetFingerprint(const char * ns,const char * tag,const dng_fingerprint & print,bool allowInvalid)1811 void 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 
SetVersion2to4(const char * ns,const char * path,uint32 version)1837 void 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 
GetIPTCDigest() const1889 dng_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 
SetIPTCDigest(dng_fingerprint & digest)1909 void dng_xmp::SetIPTCDigest (dng_fingerprint &digest)
1910 	{
1911 
1912 	SetFingerprint (XMP_NS_PHOTOSHOP,
1913 					"LegacyIPTCDigest",
1914 					digest);
1915 
1916 	}
1917 
1918 /******************************************************************************/
1919 
ClearIPTCDigest()1920 void dng_xmp::ClearIPTCDigest ()
1921 	{
1922 
1923 	Remove (XMP_NS_PHOTOSHOP, "LegacyIPTCDigest");
1924 
1925 	}
1926 
1927 /*****************************************************************************/
1928 
SyncIPTC(dng_iptc & iptc,uint32 options)1929 void 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 
IngestIPTC(dng_metadata & metadata,bool xmpIsNewer)2093 void 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 
RebuildIPTC(dng_metadata & metadata,dng_memory_allocator & allocator,bool padForTIFF)2184 void 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 
SyncFlash(uint32 & flashState,uint32 & flashMask,uint32 options)2218 void 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 
SyncExif(dng_exif & exif,const dng_exif * originalExif,bool doingUpdateFromXMP,bool removeFromXMP)2393 void 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 
SyncApproximateFocusDistance(dng_exif & exif,const uint32 readOnly)3894 void 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 
ValidateStringList(const char * ns,const char * path)3907 void dng_xmp::ValidateStringList (const char *ns,
3908 							      const char *path)
3909 	{
3910 
3911 	fSDK->ValidateStringList (ns, path);
3912 
3913 	}
3914 
3915 /******************************************************************************/
3916 
ValidateMetadata()3917 void 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 
DateTimeIsDateOnly(const char * ns,const char * path)3933 bool 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 
UpdateExifDates(dng_exif & exif,bool removeFromXMP)3971 void 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 
UpdateDateTime(const dng_date_time_info & dt)4101 void 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 
UpdateMetadataDate(const dng_date_time_info & dt)4114 void 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 
HasOrientation() const4127 bool 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 
GetOrientation() const4147 dng_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 
ClearOrientation()4174 void dng_xmp::ClearOrientation ()
4175 	{
4176 
4177 	fSDK->Remove (XMP_NS_TIFF, "Orientation");
4178 
4179 	}
4180 
4181 /******************************************************************************/
4182 
SetOrientation(const dng_orientation & orientation)4183 void 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 
SyncOrientation(dng_negative & negative,bool xmpIsMaster)4194 void dng_xmp::SyncOrientation (dng_negative &negative,
4195 					   		   bool xmpIsMaster)
4196 	{
4197 
4198 	SyncOrientation (negative.Metadata (), xmpIsMaster);
4199 
4200 	}
4201 
4202 /*****************************************************************************/
4203 
SyncOrientation(dng_metadata & metadata,bool xmpIsMaster)4204 void 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 
ClearImageInfo()4232 void 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 
SetImageSize(const dng_point & size)4265 void 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 
SetSampleInfo(uint32 samplesPerPixel,uint32 bitsPerSample)4280 void 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 
SetPhotometricInterpretation(uint32 pi)4307 void dng_xmp::SetPhotometricInterpretation (uint32 pi)
4308 	{
4309 
4310 	Set_uint32 (XMP_NS_TIFF, "PhotometricInterpretation", pi);
4311 
4312 	}
4313 
4314 /******************************************************************************/
4315 
SetResolution(const dng_resolution & res)4316 void 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 
ComposeArrayItemPath(const char * ns,const char * arrayName,int32 itemNumber,dng_string & s) const4328 void 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 
ComposeStructFieldPath(const char * ns,const char * structName,const char * fieldNS,const char * fieldName,dng_string & s) const4340 void 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 
CountArrayItems(const char * ns,const char * path) const4353 int32 dng_xmp::CountArrayItems (const char *ns,
4354 							    const char *path) const
4355 	{
4356 
4357 	return fSDK->CountArrayItems (ns, path);
4358 
4359 	}
4360 
4361 /*****************************************************************************/
4362 
AppendArrayItem(const char * ns,const char * arrayName,const char * itemValue,bool isBag,bool propIsStruct)4363 void 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 
DocOpsOpenXMP(const char * srcMIMI)4383 void dng_xmp::DocOpsOpenXMP (const char *srcMIMI)
4384 	{
4385 
4386 	fSDK->DocOpsOpenXMP (srcMIMI);
4387 
4388 	}
4389 
4390 /*****************************************************************************/
4391 
DocOpsPrepareForSave(const char * srcMIMI,const char * dstMIMI,bool newPath)4392 void 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 
DocOpsUpdateMetadata(const char * srcMIMI)4405 void dng_xmp::DocOpsUpdateMetadata (const char *srcMIMI)
4406 	{
4407 
4408 	fSDK->DocOpsUpdateMetadata (srcMIMI);
4409 
4410 	}
4411 
4412 /*****************************************************************************/
4413 
4414 #endif
4415 
4416 #endif
4417 /*****************************************************************************/
4418