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