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