File indexing completed on 2025-01-19 03:55:19
0001 /*****************************************************************************/ 0002 // Copyright 2006-2019 Adobe Systems Incorporated 0003 // All Rights Reserved. 0004 // 0005 // NOTICE: Adobe permits you to use, modify, and distribute this file in 0006 // accordance with the terms of the Adobe license agreement accompanying it. 0007 /*****************************************************************************/ 0008 0009 #include "dng_xmp.h" 0010 0011 #include "dng_assertions.h" 0012 #include "dng_date_time.h" 0013 #include "dng_exceptions.h" 0014 #include "dng_exif.h" 0015 #include "dng_image_writer.h" 0016 #include "dng_iptc.h" 0017 #include "dng_negative.h" 0018 #include "dng_string.h" 0019 #include "dng_string_list.h" 0020 #include "dng_utils.h" 0021 #include "dng_xmp_sdk.h" 0022 0023 /*****************************************************************************/ 0024 0025 dng_xmp::dng_xmp (dng_memory_allocator &allocator) 0026 0027 : fAllocator (allocator) 0028 0029 , fSDK (NULL) 0030 0031 { 0032 0033 fSDK = new dng_xmp_sdk (); 0034 0035 if (!fSDK) 0036 { 0037 ThrowMemoryFull (); 0038 } 0039 0040 } 0041 0042 /*****************************************************************************/ 0043 0044 dng_xmp::dng_xmp (const dng_xmp &xmp) 0045 0046 : fAllocator (xmp.fAllocator) 0047 0048 , fSDK (NULL) 0049 0050 { 0051 0052 fSDK = new dng_xmp_sdk (*xmp.fSDK); 0053 0054 if (!fSDK) 0055 { 0056 ThrowMemoryFull (); 0057 } 0058 0059 } 0060 0061 /*****************************************************************************/ 0062 0063 dng_xmp::~dng_xmp () 0064 { 0065 0066 if (fSDK) 0067 { 0068 0069 delete fSDK; 0070 0071 } 0072 0073 } 0074 0075 /*****************************************************************************/ 0076 0077 dng_xmp * dng_xmp::Clone () const 0078 { 0079 0080 dng_xmp *result = new dng_xmp (*this); 0081 0082 if (!result) 0083 { 0084 ThrowMemoryFull (); 0085 } 0086 0087 return result; 0088 0089 } 0090 0091 /*****************************************************************************/ 0092 0093 void dng_xmp::TrimDecimal (char *s) 0094 { 0095 0096 uint32 len = (uint32) strlen (s); 0097 0098 while (len > 0) 0099 { 0100 0101 if (s [len - 1] == '0') 0102 s [--len] = 0; 0103 0104 else 0105 break; 0106 0107 } 0108 0109 if (len > 0) 0110 { 0111 0112 if (s [len - 1] == '.') 0113 s [--len] = 0; 0114 0115 } 0116 0117 } 0118 0119 /*****************************************************************************/ 0120 0121 dng_string dng_xmp::EncodeFingerprint (const dng_fingerprint &f, 0122 bool allowInvalid) 0123 { 0124 0125 dng_string result; 0126 0127 if (f.IsValid () || allowInvalid) 0128 { 0129 0130 char s [dng_fingerprint::kDNGFingerprintSize * 2 + 1]; 0131 0132 f.ToUtf8HexString (s); 0133 0134 result.Set (s); 0135 0136 } 0137 0138 return result; 0139 0140 } 0141 0142 /*****************************************************************************/ 0143 0144 dng_fingerprint dng_xmp::DecodeFingerprint (const dng_string &s) 0145 { 0146 0147 dng_fingerprint result; 0148 0149 if (s.Length () == 32) 0150 result.FromUtf8HexString (s.Get ()); 0151 0152 return result; 0153 0154 } 0155 0156 /*****************************************************************************/ 0157 0158 dng_string dng_xmp::EncodeGPSVersion (uint32 version) 0159 { 0160 0161 dng_string result; 0162 0163 if (version) 0164 { 0165 0166 uint8 b0 = (uint8) (version >> 24); 0167 uint8 b1 = (uint8) (version >> 16); 0168 uint8 b2 = (uint8) (version >> 8); 0169 uint8 b3 = (uint8) (version ); 0170 0171 if (b0 <= 9 && b1 <= 9 && b2 <= 9 && b3 <= 9) 0172 { 0173 0174 char s [32]; 0175 0176 sprintf (s, 0177 "%u.%u.%u.%u", 0178 (unsigned) b0, 0179 (unsigned) b1, 0180 (unsigned) b2, 0181 (unsigned) b3); 0182 0183 result.Set (s); 0184 0185 } 0186 0187 } 0188 0189 return result; 0190 0191 } 0192 0193 /*****************************************************************************/ 0194 0195 uint32 dng_xmp::DecodeGPSVersion (const dng_string &s) 0196 { 0197 0198 uint32 result = 0; 0199 0200 if (s.Length () == 7) 0201 { 0202 0203 unsigned b0 = 0; 0204 unsigned b1 = 0; 0205 unsigned b2 = 0; 0206 unsigned b3 = 0; 0207 0208 if (sscanf (s.Get (), 0209 "%u.%u.%u.%u", 0210 &b0, 0211 &b1, 0212 &b2, 0213 &b3) == 4) 0214 { 0215 0216 result = (b0 << 24) | 0217 (b1 << 16) | 0218 (b2 << 8) | 0219 (b3 ); 0220 0221 } 0222 0223 } 0224 0225 return result; 0226 0227 } 0228 0229 /*****************************************************************************/ 0230 0231 dng_string dng_xmp::EncodeGPSCoordinate (const dng_string &ref, 0232 const dng_urational *coord) 0233 { 0234 0235 dng_string result; 0236 0237 if (ref.Length () == 1 && coord [0].IsValid () && 0238 coord [1].IsValid ()) 0239 { 0240 0241 char refChar = ForceUppercase (ref.Get () [0]); 0242 0243 if (refChar == 'N' || 0244 refChar == 'S' || 0245 refChar == 'E' || 0246 refChar == 'W') 0247 { 0248 0249 char s [256]; 0250 0251 // Use the seconds case if all three values are 0252 // integers. 0253 0254 if (coord [0].d == 1 && 0255 coord [1].d == 1 && 0256 coord [2].d == 1) 0257 { 0258 0259 sprintf (s, 0260 "%u,%u,%u%c", 0261 (unsigned) coord [0].n, 0262 (unsigned) coord [1].n, 0263 (unsigned) coord [2].n, 0264 refChar); 0265 0266 } 0267 0268 // Else we need to use the fractional minutes case. 0269 0270 else 0271 { 0272 0273 // Find value minutes. 0274 0275 real64 x = coord [0].As_real64 () * 60.0 + 0276 coord [1].As_real64 () + 0277 coord [2].As_real64 () * (1.0 / 60.0); 0278 0279 // Round to fractional seven decimal places. 0280 0281 uint64 y = (uint64) Round_int64 (x * 10000000.0); 0282 0283 // Split into degrees and minutes. 0284 0285 uint32 d = (uint32) (y / (60 * 10000000)); 0286 uint32 m = (uint32) (y % (60 * 10000000)); 0287 0288 char min [32]; 0289 0290 sprintf (min, "%.7f", m * (1.0 / 10000000.0)); 0291 0292 TrimDecimal (min); 0293 0294 sprintf (s, 0295 "%u,%s%c", 0296 (unsigned) d, 0297 min, 0298 refChar); 0299 0300 } 0301 0302 result.Set (s); 0303 0304 } 0305 0306 } 0307 0308 return result; 0309 0310 } 0311 0312 /*****************************************************************************/ 0313 0314 void dng_xmp::DecodeGPSCoordinate (const dng_string &s, 0315 dng_string &ref, 0316 dng_urational *coord) 0317 { 0318 0319 ref.Clear (); 0320 0321 coord [0].Clear (); 0322 coord [1].Clear (); 0323 coord [2].Clear (); 0324 0325 if (s.Length () > 1) 0326 { 0327 0328 char refChar = ForceUppercase (s.Get () [s.Length () - 1]); 0329 0330 if (refChar == 'N' || 0331 refChar == 'S' || 0332 refChar == 'E' || 0333 refChar == 'W') 0334 { 0335 0336 dng_string ss (s); 0337 0338 ss.Truncate (ss.Length () - 1); 0339 0340 ss.NormalizeAsCommaSeparatedNumbers (); 0341 0342 int degrees = 0; 0343 0344 real64 minutes = 0.0; 0345 real64 seconds = 0.0; 0346 0347 int count = sscanf (ss.Get (), 0348 "%d,%lf,%lf", 0349 °rees, 0350 &minutes, 0351 &seconds); 0352 0353 if (count < 1) 0354 { 0355 return; 0356 } 0357 0358 // The degree, minute, second values should always be positive. 0359 0360 if (degrees < 0 || minutes < 0 || seconds < 0) 0361 { 0362 return; 0363 } 0364 0365 coord [0] = dng_urational ((uint32) degrees, 1); 0366 0367 if (count <= 2) 0368 { 0369 coord [1].Set_real64 (minutes, 10000000); 0370 coord [2] = dng_urational (0, 1); 0371 } 0372 else 0373 { 0374 coord [1].Set_real64 (minutes, 1); 0375 coord [2].Set_real64 (seconds, 100000); 0376 } 0377 0378 char r [2]; 0379 0380 r [0] = refChar; 0381 r [1] = 0; 0382 0383 ref.Set (r); 0384 0385 } 0386 0387 } 0388 0389 } 0390 0391 /*****************************************************************************/ 0392 0393 dng_string dng_xmp::EncodeGPSDateTime (const dng_string &dateStamp, 0394 const dng_urational *timeStamp) 0395 { 0396 0397 dng_string result; 0398 0399 if (timeStamp [0].IsValid () && 0400 timeStamp [1].IsValid () && 0401 timeStamp [2].IsValid ()) 0402 { 0403 0404 char s [256]; 0405 0406 char sec [32]; 0407 0408 sprintf (sec, 0409 "%09.6f", 0410 timeStamp [2].As_real64 ()); 0411 0412 TrimDecimal (sec); 0413 0414 int year = 0; 0415 int month = 0; 0416 int day = 0; 0417 0418 if (dateStamp.NotEmpty ()) 0419 { 0420 0421 sscanf (dateStamp.Get (), 0422 "%d:%d:%d", 0423 &year, 0424 &month, 0425 &day); 0426 0427 } 0428 0429 if (year >= 1 && year <= 9999 && 0430 month >= 1 && month <= 12 && 0431 day >= 1 && day <= 31) 0432 { 0433 0434 sprintf (s, 0435 "%04d-%02d-%02dT%02u:%02u:%sZ", 0436 year, 0437 month, 0438 day, 0439 (unsigned) Round_uint32 (timeStamp [0].As_real64 ()), 0440 (unsigned) Round_uint32 (timeStamp [1].As_real64 ()), 0441 sec); 0442 0443 } 0444 0445 else 0446 { 0447 0448 sprintf (s, 0449 "%02u:%02u:%sZ", 0450 (unsigned) Round_uint32 (timeStamp [0].As_real64 ()), 0451 (unsigned) Round_uint32 (timeStamp [1].As_real64 ()), 0452 sec); 0453 0454 } 0455 0456 result.Set (s); 0457 0458 } 0459 0460 return result; 0461 0462 } 0463 0464 /*****************************************************************************/ 0465 0466 void dng_xmp::DecodeGPSDateTime (const dng_string &s, 0467 dng_string &dateStamp, 0468 dng_urational *timeStamp) 0469 { 0470 0471 dateStamp.Clear (); 0472 0473 timeStamp [0].Clear (); 0474 timeStamp [1].Clear (); 0475 timeStamp [2].Clear (); 0476 0477 if (s.NotEmpty ()) 0478 { 0479 0480 unsigned year = 0; 0481 unsigned month = 0; 0482 unsigned day = 0; 0483 unsigned hour = 0; 0484 unsigned minute = 0; 0485 0486 double second = 0.0; 0487 0488 if (sscanf (s.Get (), 0489 "%u-%u-%uT%u:%u:%lf", 0490 &year, 0491 &month, 0492 &day, 0493 &hour, 0494 &minute, 0495 &second) == 6) 0496 { 0497 0498 if (year >= 1 && year <= 9999 && 0499 month >= 1 && month <= 12 && 0500 day >= 1 && day <= 31 ) 0501 { 0502 0503 char ss [64]; 0504 0505 sprintf (ss, 0506 "%04u:%02u:%02u", 0507 year, 0508 month, 0509 day); 0510 0511 dateStamp.Set (ss); 0512 0513 } 0514 0515 } 0516 0517 else if (sscanf (s.Get (), 0518 "%u:%u:%lf", 0519 &hour, 0520 &minute, 0521 &second) != 3) 0522 { 0523 0524 return; 0525 0526 } 0527 0528 timeStamp [0] = dng_urational ((uint32) hour , 1); 0529 timeStamp [1] = dng_urational ((uint32) minute, 1); 0530 0531 timeStamp [2].Set_real64 (second, 1000); 0532 0533 } 0534 0535 } 0536 0537 /*****************************************************************************/ 0538 0539 void dng_xmp::Parse (dng_host &host, 0540 const void *buffer, 0541 uint32 count) 0542 { 0543 0544 fSDK->Parse (host, 0545 (const char *) buffer, 0546 count); 0547 0548 } 0549 0550 /*****************************************************************************/ 0551 0552 dng_memory_block * dng_xmp::Serialize (bool asPacket, 0553 uint32 targetBytes, 0554 uint32 padBytes, 0555 bool forJPEG, 0556 bool compact) const 0557 { 0558 0559 return fSDK->Serialize (fAllocator, 0560 asPacket, 0561 targetBytes, 0562 padBytes, 0563 forJPEG, 0564 compact); 0565 0566 } 0567 0568 /*****************************************************************************/ 0569 0570 void dng_xmp::PackageForJPEG (AutoPtr<dng_memory_block> &stdBlock, 0571 AutoPtr<dng_memory_block> &extBlock, 0572 dng_string &extDigest) const 0573 { 0574 0575 fSDK->PackageForJPEG (fAllocator, 0576 stdBlock, 0577 extBlock, 0578 extDigest); 0579 0580 } 0581 0582 /*****************************************************************************/ 0583 0584 void dng_xmp::MergeFromJPEG (const dng_xmp &xmp) 0585 { 0586 0587 fSDK->MergeFromJPEG (xmp.fSDK); 0588 0589 } 0590 0591 /*****************************************************************************/ 0592 0593 bool dng_xmp::HasMeta () const 0594 { 0595 0596 return fSDK->HasMeta (); 0597 0598 } 0599 0600 /*****************************************************************************/ 0601 0602 void * dng_xmp::GetPrivateMeta () 0603 { 0604 0605 return fSDK->GetPrivateMeta (); 0606 0607 } 0608 0609 /*****************************************************************************/ 0610 0611 bool dng_xmp::Exists (const char *ns, 0612 const char *path) const 0613 { 0614 0615 return fSDK->Exists (ns, path); 0616 0617 } 0618 0619 /*****************************************************************************/ 0620 0621 bool dng_xmp::HasNameSpace (const char *ns) const 0622 { 0623 0624 return fSDK->HasNameSpace (ns); 0625 0626 } 0627 0628 /*****************************************************************************/ 0629 0630 bool dng_xmp::IteratePaths (IteratePathsCallback *callback, 0631 void *callbackData, 0632 const char *ns, 0633 const char *path) 0634 { 0635 0636 return fSDK->IteratePaths (callback, callbackData, ns, path); 0637 0638 } 0639 0640 /*****************************************************************************/ 0641 0642 void dng_xmp::Remove (const char *ns, 0643 const char *path) 0644 { 0645 0646 fSDK->Remove (ns, path); 0647 0648 } 0649 0650 /*****************************************************************************/ 0651 0652 void dng_xmp::RemoveProperties (const char *ns) 0653 { 0654 0655 fSDK->RemoveProperties (ns); 0656 0657 } 0658 0659 /*****************************************************************************/ 0660 0661 void dng_xmp::RemoveEmptyStringOrArray (const char *ns, 0662 const char *path) 0663 { 0664 0665 if (path == NULL || path [0] == 0) 0666 { 0667 return; 0668 } 0669 0670 if (fSDK->IsEmptyString (ns, path) || 0671 fSDK->IsEmptyArray (ns, path)) 0672 { 0673 0674 Remove (ns, path); 0675 0676 } 0677 0678 } 0679 0680 /*****************************************************************************/ 0681 0682 static bool RemoveEmptyStringsAndArraysCallback (const char *ns, 0683 const char *path, 0684 void *callbackData) 0685 { 0686 0687 dng_xmp *xmp = (dng_xmp *) callbackData; 0688 0689 xmp->RemoveEmptyStringOrArray (ns, path); 0690 0691 return true; 0692 0693 } 0694 0695 /*****************************************************************************/ 0696 0697 void dng_xmp::RemoveEmptyStringsAndArrays (const char *ns) 0698 { 0699 0700 IteratePaths (RemoveEmptyStringsAndArraysCallback, 0701 (void *) this, 0702 ns, 0703 NULL); 0704 0705 } 0706 0707 /*****************************************************************************/ 0708 0709 void dng_xmp::Set (const char *ns, 0710 const char *path, 0711 const char *text) 0712 { 0713 0714 fSDK->Set (ns, path, text); 0715 0716 } 0717 0718 /*****************************************************************************/ 0719 0720 bool dng_xmp::GetString (const char *ns, 0721 const char *path, 0722 dng_string &s) const 0723 { 0724 0725 return fSDK->GetString (ns, path, s); 0726 0727 } 0728 0729 /*****************************************************************************/ 0730 0731 void dng_xmp::SetString (const char *ns, 0732 const char *path, 0733 const dng_string &s) 0734 { 0735 0736 fSDK->SetString (ns, path, s); 0737 0738 } 0739 0740 /*****************************************************************************/ 0741 0742 bool dng_xmp::SyncString (const char *ns, 0743 const char *path, 0744 dng_string &s, 0745 uint32 options) 0746 { 0747 0748 bool isDefault = s.IsEmpty (); 0749 0750 // Sync 1: Force XMP to match non-XMP. 0751 0752 if (options & ignoreXMP) 0753 { 0754 0755 if (isDefault || (options & removeXMP)) 0756 { 0757 0758 Remove (ns, path); 0759 0760 } 0761 0762 else 0763 { 0764 0765 SetString (ns, path, s); 0766 0767 } 0768 0769 return false; 0770 0771 } 0772 0773 // Sync 2: From non-XMP to XMP if non-XMP is prefered. 0774 0775 if ((options & preferNonXMP) && !isDefault) 0776 { 0777 0778 if (options & removeXMP) 0779 { 0780 0781 Remove (ns, path); 0782 0783 } 0784 0785 else 0786 { 0787 0788 SetString (ns, path, s); 0789 0790 } 0791 0792 return false; 0793 0794 } 0795 0796 // Sync 3: From XMP to non-XMP if XMP is prefered or default non-XMP. 0797 0798 if ((options & preferXMP) || isDefault) 0799 { 0800 0801 if (GetString (ns, path, s)) 0802 { 0803 0804 if (options & removeXMP) 0805 { 0806 0807 Remove (ns, path); 0808 0809 } 0810 0811 return true; 0812 0813 } 0814 0815 } 0816 0817 // Sync 4: From non-XMP to XMP. 0818 0819 if (options & removeXMP) 0820 { 0821 0822 Remove (ns, path); 0823 0824 } 0825 0826 else if (!isDefault) 0827 { 0828 0829 SetString (ns, path, s); 0830 0831 } 0832 0833 return false; 0834 0835 } 0836 0837 /*****************************************************************************/ 0838 0839 bool dng_xmp::GetStringList (const char *ns, 0840 const char *path, 0841 dng_string_list &list) const 0842 { 0843 0844 return fSDK->GetStringList (ns, path, list); 0845 0846 } 0847 0848 /*****************************************************************************/ 0849 0850 void dng_xmp::SetStringList (const char *ns, 0851 const char *path, 0852 const dng_string_list &list, 0853 bool isBag) 0854 { 0855 0856 fSDK->SetStringList (ns, path, list, isBag); 0857 0858 } 0859 0860 /*****************************************************************************/ 0861 0862 void dng_xmp::SyncStringList (const char *ns, 0863 const char *path, 0864 dng_string_list &list, 0865 bool isBag, 0866 uint32 options) 0867 { 0868 0869 bool isDefault = (list.Count () == 0); 0870 0871 // First make sure the XMP is not badly formatted, since 0872 // this breaks some Photoshop logic. 0873 0874 ValidateStringList (ns, path); 0875 0876 // Sync 1: Force XMP to match non-XMP. 0877 0878 if (options & ignoreXMP) 0879 { 0880 0881 if (isDefault) 0882 { 0883 0884 Remove (ns, path); 0885 0886 } 0887 0888 else 0889 { 0890 0891 SetStringList (ns, path, list, isBag); 0892 0893 } 0894 0895 return; 0896 0897 } 0898 0899 // Sync 2: From non-XMP to XMP if non-XMP is prefered. 0900 0901 if ((options & preferNonXMP) && !isDefault) 0902 { 0903 0904 SetStringList (ns, path, list, isBag); 0905 0906 return; 0907 0908 } 0909 0910 // Sync 3: From XMP to non-XMP if XMP is prefered or default non-XMP. 0911 0912 if ((options & preferXMP) || isDefault) 0913 { 0914 0915 if (GetStringList (ns, path, list)) 0916 { 0917 0918 return; 0919 0920 } 0921 0922 } 0923 0924 // Sync 4: From non-XMP to XMP. 0925 0926 if (!isDefault) 0927 { 0928 0929 SetStringList (ns, path, list, isBag); 0930 0931 } 0932 0933 } 0934 0935 /*****************************************************************************/ 0936 0937 void dng_xmp::SetStructField (const char *ns, 0938 const char *path, 0939 const char *fieldNS, 0940 const char *fieldName, 0941 const dng_string &s) 0942 { 0943 0944 dng_string ss (s); 0945 0946 ss.SetLineEndings ('\n'); 0947 0948 ss.StripLowASCII (); 0949 0950 fSDK->SetStructField (ns, path, fieldNS, fieldName, ss.Get ()); 0951 0952 } 0953 0954 /*****************************************************************************/ 0955 0956 void dng_xmp::SetStructField (const char *ns, 0957 const char *path, 0958 const char *fieldNS, 0959 const char *fieldName, 0960 const char *s) 0961 { 0962 0963 fSDK->SetStructField (ns, path, fieldNS, fieldName, s); 0964 0965 } 0966 0967 /*****************************************************************************/ 0968 0969 void dng_xmp::DeleteStructField (const char *ns, 0970 const char *path, 0971 const char *fieldNS, 0972 const char *fieldName) 0973 { 0974 0975 fSDK->DeleteStructField (ns, path, fieldNS, fieldName); 0976 0977 } 0978 0979 /*****************************************************************************/ 0980 0981 bool dng_xmp::GetStructField (const char *ns, 0982 const char *path, 0983 const char *fieldNS, 0984 const char *fieldName, 0985 dng_string &s) const 0986 { 0987 0988 return fSDK->GetStructField (ns, path, fieldNS, fieldName, s); 0989 0990 } 0991 0992 /*****************************************************************************/ 0993 0994 void dng_xmp::SetAltLangDefault (const char *ns, 0995 const char *path, 0996 const dng_string &s) 0997 { 0998 0999 fSDK->SetAltLangDefault (ns, path, s); 1000 1001 } 1002 1003 /*****************************************************************************/ 1004 1005 void dng_xmp::SetLocalString (const char *ns, 1006 const char *path, 1007 const dng_local_string &s) 1008 { 1009 1010 fSDK->SetLocalString (ns, path, s); 1011 1012 } 1013 1014 /*****************************************************************************/ 1015 1016 bool dng_xmp::GetAltLangDefault (const char *ns, 1017 const char *path, 1018 dng_string &s, 1019 bool silent) const 1020 { 1021 1022 return fSDK->GetAltLangDefault (ns, path, s, silent); 1023 1024 } 1025 1026 /*****************************************************************************/ 1027 1028 bool dng_xmp::GetLocalString (const char *ns, 1029 const char *path, 1030 dng_local_string &s) const 1031 { 1032 1033 return fSDK->GetLocalString (ns, path, s); 1034 1035 } 1036 1037 /*****************************************************************************/ 1038 1039 bool dng_xmp::SyncAltLangDefault (const char *ns, 1040 const char *path, 1041 dng_string &s, 1042 uint32 options) 1043 { 1044 1045 bool isDefault = s.IsEmpty (); 1046 1047 // Sync 1: Force XMP to match non-XMP. 1048 1049 if (options & ignoreXMP) 1050 { 1051 1052 if (isDefault) 1053 { 1054 1055 Remove (ns, path); 1056 1057 } 1058 1059 else 1060 { 1061 1062 SetAltLangDefault (ns, path, s); 1063 1064 } 1065 1066 return false; 1067 1068 } 1069 1070 // Sync 2: From non-XMP to XMP if non-XMP is prefered. 1071 1072 if ((options & preferNonXMP) && !isDefault) 1073 { 1074 1075 SetAltLangDefault (ns, path, s); 1076 1077 return false; 1078 1079 } 1080 1081 // Sync 3: From XMP to non-XMP if XMP is prefered or default non-XMP. 1082 1083 if ((options & preferXMP) || isDefault) 1084 { 1085 1086 if (GetAltLangDefault (ns, path, s)) 1087 { 1088 1089 return true; 1090 1091 } 1092 1093 } 1094 1095 // Sync 4: From non-XMP to XMP. 1096 1097 if (!isDefault) 1098 { 1099 1100 SetAltLangDefault (ns, path, s); 1101 1102 } 1103 1104 return false; 1105 1106 } 1107 1108 /*****************************************************************************/ 1109 1110 bool dng_xmp::GetBoolean (const char *ns, 1111 const char *path, 1112 bool &x) const 1113 { 1114 1115 dng_string s; 1116 1117 if (GetString (ns, path, s)) 1118 { 1119 1120 if (s.Matches ("True")) 1121 { 1122 1123 x = true; 1124 1125 return true; 1126 1127 } 1128 1129 if (s.Matches ("False")) 1130 { 1131 1132 x = false; 1133 1134 return true; 1135 1136 } 1137 1138 } 1139 1140 return false; 1141 1142 } 1143 1144 /*****************************************************************************/ 1145 1146 void dng_xmp::SetBoolean (const char *ns, 1147 const char *path, 1148 bool x) 1149 { 1150 1151 Set (ns, path, x ? "True" : "False"); 1152 1153 } 1154 1155 /*****************************************************************************/ 1156 1157 bool dng_xmp::Get_int32 (const char *ns, 1158 const char *path, 1159 int32 &x) const 1160 { 1161 1162 dng_string s; 1163 1164 if (GetString (ns, path, s)) 1165 { 1166 1167 if (s.NotEmpty ()) 1168 { 1169 1170 int y = 0; 1171 1172 if (sscanf (s.Get (), "%d", &y) == 1) 1173 { 1174 1175 x = y; 1176 1177 return true; 1178 1179 } 1180 1181 } 1182 1183 } 1184 1185 return false; 1186 1187 } 1188 1189 /*****************************************************************************/ 1190 1191 void dng_xmp::Set_int32 (const char *ns, 1192 const char *path, 1193 int32 x, 1194 bool usePlus) 1195 { 1196 1197 char s [64]; 1198 1199 if (x > 0 && usePlus) 1200 { 1201 sprintf (s, "+%d", (int) x); 1202 } 1203 else 1204 { 1205 sprintf (s, "%d", (int) x); 1206 } 1207 1208 Set (ns, path, s); 1209 1210 } 1211 1212 /*****************************************************************************/ 1213 1214 bool dng_xmp::Get_uint32 (const char *ns, 1215 const char *path, 1216 uint32 &x) const 1217 { 1218 1219 dng_string s; 1220 1221 if (GetString (ns, path, s)) 1222 { 1223 1224 if (s.NotEmpty ()) 1225 { 1226 1227 unsigned y = 0; 1228 1229 if (sscanf (s.Get (), "%u", &y) == 1) 1230 { 1231 1232 x = y; 1233 1234 return true; 1235 1236 } 1237 1238 } 1239 1240 } 1241 1242 return false; 1243 1244 } 1245 1246 /*****************************************************************************/ 1247 1248 void dng_xmp::Set_uint32 (const char *ns, 1249 const char *path, 1250 uint32 x) 1251 { 1252 1253 char s [64]; 1254 1255 sprintf (s, 1256 "%u", 1257 (unsigned) x); 1258 1259 Set (ns, path, s); 1260 1261 } 1262 1263 /*****************************************************************************/ 1264 1265 void dng_xmp::Sync_uint32 (const char *ns, 1266 const char *path, 1267 uint32 &x, 1268 bool isDefault, 1269 uint32 options) 1270 { 1271 1272 // Sync 1: Force XMP to match non-XMP. 1273 1274 if (options & ignoreXMP) 1275 { 1276 1277 if (isDefault || (options & removeXMP)) 1278 { 1279 1280 Remove (ns, path); 1281 1282 } 1283 1284 else 1285 { 1286 1287 Set_uint32 (ns, path, x); 1288 1289 } 1290 1291 return; 1292 1293 } 1294 1295 // Sync 2: From non-XMP to XMP if non-XMP is prefered. 1296 1297 if ((options & preferNonXMP) && !isDefault) 1298 { 1299 1300 if (options & removeXMP) 1301 { 1302 1303 Remove (ns, path); 1304 1305 } 1306 1307 else 1308 { 1309 1310 Set_uint32 (ns, path, x); 1311 1312 } 1313 1314 return; 1315 1316 } 1317 1318 // Sync 3: From XMP to non-XMP if XMP is prefered or default non-XMP. 1319 1320 if ((options & preferXMP) || isDefault) 1321 { 1322 1323 if (Get_uint32 (ns, path, x)) 1324 { 1325 1326 if (options & removeXMP) 1327 { 1328 1329 Remove (ns, path); 1330 1331 } 1332 1333 return; 1334 1335 } 1336 1337 } 1338 1339 // Sync 4: From non-XMP to XMP. 1340 1341 if (options & removeXMP) 1342 { 1343 1344 Remove (ns, path); 1345 1346 } 1347 1348 else if (!isDefault) 1349 { 1350 1351 Set_uint32 (ns, path, x); 1352 1353 } 1354 1355 } 1356 1357 /*****************************************************************************/ 1358 1359 void dng_xmp::Sync_uint32_array (const char *ns, 1360 const char *path, 1361 uint32 *data, 1362 uint32 &count, 1363 uint32 maxCount, 1364 uint32 options) 1365 { 1366 1367 dng_string_list list; 1368 1369 for (uint32 j = 0; j < count; j++) 1370 { 1371 1372 char s [32]; 1373 1374 sprintf (s, "%u", (unsigned) data [j]); 1375 1376 dng_string ss; 1377 1378 ss.Set (s); 1379 1380 list.Append (ss); 1381 1382 } 1383 1384 SyncStringList (ns, 1385 path, 1386 list, 1387 false, 1388 options); 1389 1390 count = 0; 1391 1392 for (uint32 k = 0; k < maxCount; k++) 1393 { 1394 1395 data [k] = 0; 1396 1397 if (k < list.Count ()) 1398 { 1399 1400 unsigned x = 0; 1401 1402 if (sscanf (list [k].Get (), "%u", &x) == 1) 1403 { 1404 1405 data [count++] = x; 1406 1407 } 1408 1409 } 1410 1411 } 1412 1413 } 1414 1415 /*****************************************************************************/ 1416 1417 bool dng_xmp::Get_real64 (const char *ns, 1418 const char *path, 1419 real64 &x) const 1420 { 1421 1422 dng_string s; 1423 1424 if (GetString (ns, path, s)) 1425 { 1426 1427 if (s.NotEmpty ()) 1428 { 1429 1430 double y = 0; 1431 1432 if (sscanf (s.Get (), "%lf", &y) == 1) 1433 { 1434 1435 x = y; 1436 1437 return true; 1438 1439 } 1440 1441 } 1442 1443 } 1444 1445 return false; 1446 1447 } 1448 1449 /*****************************************************************************/ 1450 1451 void dng_xmp::Set_real64 (const char *ns, 1452 const char *path, 1453 real64 x, 1454 uint32 places, 1455 bool trim, 1456 bool usePlus) 1457 { 1458 1459 char s [64]; 1460 1461 if (x > 0.0 && usePlus) 1462 { 1463 sprintf (s, "+%0.*f", (unsigned) places, (double) x); 1464 } 1465 else 1466 { 1467 sprintf (s, "%0.*f", (unsigned) places, (double) x); 1468 } 1469 1470 if (trim) 1471 { 1472 1473 while (s [strlen (s) - 1] == '0') 1474 { 1475 s [strlen (s) - 1] = 0; 1476 } 1477 1478 if (s [strlen (s) - 1] == '.') 1479 { 1480 s [strlen (s) - 1] = 0; 1481 } 1482 1483 } 1484 1485 Set (ns, path, s); 1486 1487 } 1488 1489 /*****************************************************************************/ 1490 1491 bool dng_xmp::Get_urational (const char *ns, 1492 const char *path, 1493 dng_urational &r) const 1494 { 1495 1496 dng_string s; 1497 1498 if (GetString (ns, path, s)) 1499 { 1500 1501 if (s.NotEmpty ()) 1502 { 1503 1504 unsigned n = 0; 1505 unsigned d = 0; 1506 1507 if (sscanf (s.Get (), "%u/%u", &n, &d) == 2) 1508 { 1509 1510 if (d != 0) 1511 { 1512 1513 r = dng_urational (n, d); 1514 1515 return true; 1516 1517 } 1518 1519 } 1520 1521 } 1522 1523 } 1524 1525 return false; 1526 1527 } 1528 1529 /*****************************************************************************/ 1530 1531 void dng_xmp::Set_urational (const char *ns, 1532 const char *path, 1533 const dng_urational &r) 1534 { 1535 1536 char s [64]; 1537 1538 sprintf (s, 1539 "%u/%u", 1540 (unsigned) r.n, 1541 (unsigned) r.d); 1542 1543 Set (ns, path, s); 1544 1545 } 1546 1547 /*****************************************************************************/ 1548 1549 void dng_xmp::Sync_urational (const char *ns, 1550 const char *path, 1551 dng_urational &r, 1552 uint32 options) 1553 { 1554 1555 bool isDefault = r.NotValid (); 1556 1557 // Sync 1: Force XMP to match non-XMP. 1558 1559 if (options & ignoreXMP) 1560 { 1561 1562 if (isDefault || (options & removeXMP)) 1563 { 1564 1565 Remove (ns, path); 1566 1567 } 1568 1569 else 1570 { 1571 1572 Set_urational (ns, path, r); 1573 1574 } 1575 1576 return; 1577 1578 } 1579 1580 // Sync 2: From non-XMP to XMP if non-XMP is prefered. 1581 1582 if ((options & preferNonXMP) && !isDefault) 1583 { 1584 1585 if (options & removeXMP) 1586 { 1587 1588 Remove (ns, path); 1589 1590 } 1591 1592 else 1593 { 1594 1595 Set_urational (ns, path, r); 1596 1597 } 1598 1599 return; 1600 1601 } 1602 1603 // Sync 3: From XMP to non-XMP if XMP is prefered or default non-XMP. 1604 1605 if ((options & preferXMP) || isDefault) 1606 { 1607 1608 if (Get_urational (ns, path, r)) 1609 { 1610 1611 if (options & removeXMP) 1612 { 1613 1614 Remove (ns, path); 1615 1616 } 1617 1618 return; 1619 1620 } 1621 1622 } 1623 1624 // Sync 4: From non-XMP to XMP. 1625 1626 if (options & removeXMP) 1627 { 1628 1629 Remove (ns, path); 1630 1631 } 1632 1633 else if (!isDefault) 1634 { 1635 1636 Set_urational (ns, path, r); 1637 1638 } 1639 1640 } 1641 1642 /*****************************************************************************/ 1643 1644 bool dng_xmp::Get_srational (const char *ns, 1645 const char *path, 1646 dng_srational &r) const 1647 { 1648 1649 dng_string s; 1650 1651 if (GetString (ns, path, s)) 1652 { 1653 1654 if (s.NotEmpty ()) 1655 { 1656 1657 int n = 0; 1658 int d = 0; 1659 1660 if (sscanf (s.Get (), "%d/%d", &n, &d) == 2) 1661 { 1662 1663 if (d != 0) 1664 { 1665 1666 r = dng_srational (n, d); 1667 1668 return true; 1669 1670 } 1671 1672 } 1673 1674 } 1675 1676 } 1677 1678 return false; 1679 1680 } 1681 1682 /*****************************************************************************/ 1683 1684 void dng_xmp::Set_srational (const char *ns, 1685 const char *path, 1686 const dng_srational &r) 1687 { 1688 1689 char s [64]; 1690 1691 sprintf (s, 1692 "%d/%d", 1693 (int) r.n, 1694 (int) r.d); 1695 1696 Set (ns, path, s); 1697 1698 } 1699 1700 /*****************************************************************************/ 1701 1702 void dng_xmp::Sync_srational (const char *ns, 1703 const char *path, 1704 dng_srational &r, 1705 uint32 options) 1706 { 1707 1708 bool isDefault = r.NotValid (); 1709 1710 // Sync 1: Force XMP to match non-XMP. 1711 1712 if (options & ignoreXMP) 1713 { 1714 1715 if (isDefault || (options & removeXMP)) 1716 { 1717 1718 Remove (ns, path); 1719 1720 } 1721 1722 else 1723 { 1724 1725 Set_srational (ns, path, r); 1726 1727 } 1728 1729 return; 1730 1731 } 1732 1733 // Sync 2: From non-XMP to XMP if non-XMP is prefered. 1734 1735 if ((options & preferNonXMP) && !isDefault) 1736 { 1737 1738 if (options & removeXMP) 1739 { 1740 1741 Remove (ns, path); 1742 1743 } 1744 1745 else 1746 { 1747 1748 Set_srational (ns, path, r); 1749 1750 } 1751 1752 return; 1753 1754 } 1755 1756 // Sync 3: From XMP to non-XMP if XMP is prefered or default non-XMP. 1757 1758 if ((options & preferXMP) || isDefault) 1759 { 1760 1761 if (Get_srational (ns, path, r)) 1762 { 1763 1764 if (options & removeXMP) 1765 { 1766 1767 Remove (ns, path); 1768 1769 } 1770 1771 return; 1772 1773 } 1774 1775 } 1776 1777 // Sync 4: From non-XMP to XMP. 1778 1779 if (options & removeXMP) 1780 { 1781 1782 Remove (ns, path); 1783 1784 } 1785 1786 else if (!isDefault) 1787 { 1788 1789 Set_srational (ns, path, r); 1790 1791 } 1792 1793 } 1794 1795 /*****************************************************************************/ 1796 1797 bool dng_xmp::GetFingerprint (const char *ns, 1798 const char *path, 1799 dng_fingerprint &print) const 1800 { 1801 1802 dng_string s; 1803 1804 if (GetString (ns, path, s)) 1805 { 1806 1807 dng_fingerprint temp = DecodeFingerprint (s); 1808 1809 if (temp.IsValid ()) 1810 { 1811 1812 print = temp; 1813 1814 return true; 1815 1816 } 1817 1818 } 1819 1820 return false; 1821 1822 } 1823 1824 /******************************************************************************/ 1825 1826 void dng_xmp::SetFingerprint (const char *ns, 1827 const char *tag, 1828 const dng_fingerprint &print, 1829 bool allowInvalid) 1830 { 1831 1832 dng_string s = EncodeFingerprint (print, allowInvalid); 1833 1834 if (s.IsEmpty ()) 1835 { 1836 1837 Remove (ns, tag); 1838 1839 } 1840 1841 else 1842 { 1843 1844 SetString (ns, tag, s); 1845 1846 } 1847 1848 } 1849 1850 /******************************************************************************/ 1851 1852 void dng_xmp::SetVersion2to4 (const char *ns, 1853 const char *path, 1854 uint32 version) 1855 { 1856 1857 char buf [32]; 1858 1859 if (version & 0x000000ff) 1860 { 1861 1862 // x.x.x.x 1863 1864 sprintf (buf, 1865 "%u.%u.%u.%u", 1866 (unsigned) ((version >> 24) & 0xff), 1867 (unsigned) ((version >> 16) & 0xff), 1868 (unsigned) ((version >> 8) & 0xff), 1869 (unsigned) ((version ) & 0xff)); 1870 1871 } 1872 1873 else if (version & 0x0000ff00) 1874 { 1875 1876 // x.x.x 1877 1878 sprintf (buf, 1879 "%u.%u.%u", 1880 (unsigned) ((version >> 24) & 0xff), 1881 (unsigned) ((version >> 16) & 0xff), 1882 (unsigned) ((version >> 8) & 0xff)); 1883 1884 } 1885 1886 else 1887 { 1888 1889 // x.x 1890 1891 sprintf (buf, 1892 "%u.%u", 1893 (unsigned) ((version >> 24) & 0xff), 1894 (unsigned) ((version >> 16) & 0xff)); 1895 1896 } 1897 1898 Set (ns, path, buf); 1899 1900 } 1901 1902 /******************************************************************************/ 1903 1904 dng_fingerprint dng_xmp::GetIPTCDigest () const 1905 { 1906 1907 dng_fingerprint digest; 1908 1909 if (GetFingerprint (XMP_NS_PHOTOSHOP, 1910 "LegacyIPTCDigest", 1911 digest)) 1912 { 1913 1914 return digest; 1915 1916 } 1917 1918 return dng_fingerprint (); 1919 1920 } 1921 1922 /******************************************************************************/ 1923 1924 void dng_xmp::SetIPTCDigest (dng_fingerprint &digest) 1925 { 1926 1927 SetFingerprint (XMP_NS_PHOTOSHOP, 1928 "LegacyIPTCDigest", 1929 digest); 1930 1931 } 1932 1933 /******************************************************************************/ 1934 1935 void dng_xmp::ClearIPTCDigest () 1936 { 1937 1938 Remove (XMP_NS_PHOTOSHOP, "LegacyIPTCDigest"); 1939 1940 } 1941 1942 /*****************************************************************************/ 1943 1944 void dng_xmp::SyncIPTC (dng_iptc &iptc, 1945 uint32 options) 1946 { 1947 1948 SyncAltLangDefault (XMP_NS_DC, 1949 "title", 1950 iptc.fTitle, 1951 options); 1952 1953 SyncString (XMP_NS_PHOTOSHOP, 1954 "Category", 1955 iptc.fCategory, 1956 options); 1957 1958 { 1959 1960 uint32 x = 0xFFFFFFFF; 1961 1962 if (iptc.fUrgency >= 0) 1963 { 1964 1965 x = (uint32) iptc.fUrgency; 1966 1967 } 1968 1969 Sync_uint32 (XMP_NS_PHOTOSHOP, 1970 "Urgency", 1971 x, 1972 x == 0xFFFFFFFF, 1973 options); 1974 1975 if (x <= 9) 1976 { 1977 1978 iptc.fUrgency = (int32) x; 1979 1980 } 1981 1982 } 1983 1984 SyncStringList (XMP_NS_PHOTOSHOP, 1985 "SupplementalCategories", 1986 iptc.fSupplementalCategories, 1987 true, 1988 options); 1989 1990 SyncStringList (XMP_NS_PHOTOSHOP, 1991 "Keywords", 1992 iptc.fKeywords, 1993 true, 1994 options); 1995 1996 SyncString (XMP_NS_PHOTOSHOP, 1997 "Instructions", 1998 iptc.fInstructions, 1999 options); 2000 2001 { 2002 2003 dng_string s = iptc.fDateTimeCreated.Encode_ISO_8601 (); 2004 2005 if (SyncString (XMP_NS_PHOTOSHOP, 2006 "DateCreated", 2007 s, 2008 options)) 2009 { 2010 2011 iptc.fDateTimeCreated.Decode_ISO_8601 (s.Get ()); 2012 2013 } 2014 2015 } 2016 2017 { 2018 2019 dng_string s = iptc.fDigitalCreationDateTime.Encode_ISO_8601 (); 2020 2021 if (SyncString (XMP_NS_EXIF, 2022 "DateTimeDigitized", 2023 s, 2024 options)) 2025 { 2026 2027 iptc.fDigitalCreationDateTime.Decode_ISO_8601 (s.Get ()); 2028 2029 } 2030 2031 } 2032 2033 SyncStringList (XMP_NS_DC, 2034 "creator", 2035 iptc.fAuthors, 2036 false, 2037 options); 2038 2039 SyncString (XMP_NS_PHOTOSHOP, 2040 "AuthorsPosition", 2041 iptc.fAuthorsPosition, 2042 options); 2043 2044 SyncString (XMP_NS_PHOTOSHOP, 2045 "City", 2046 iptc.fCity, 2047 options); 2048 2049 SyncString (XMP_NS_PHOTOSHOP, 2050 "State", 2051 iptc.fState, 2052 options); 2053 2054 SyncString (XMP_NS_PHOTOSHOP, 2055 "Country", 2056 iptc.fCountry, 2057 options); 2058 2059 SyncString (XMP_NS_IPTC, 2060 "CountryCode", 2061 iptc.fCountryCode, 2062 options); 2063 2064 SyncString (XMP_NS_IPTC, 2065 "Location", 2066 iptc.fLocation, 2067 options); 2068 2069 SyncString (XMP_NS_PHOTOSHOP, 2070 "TransmissionReference", 2071 iptc.fTransmissionReference, 2072 options); 2073 2074 SyncString (XMP_NS_PHOTOSHOP, 2075 "Headline", 2076 iptc.fHeadline, 2077 options); 2078 2079 SyncString (XMP_NS_PHOTOSHOP, 2080 "Credit", 2081 iptc.fCredit, 2082 options); 2083 2084 SyncString (XMP_NS_PHOTOSHOP, 2085 "Source", 2086 iptc.fSource, 2087 options); 2088 2089 SyncAltLangDefault (XMP_NS_DC, 2090 "rights", 2091 iptc.fCopyrightNotice, 2092 options); 2093 2094 SyncAltLangDefault (XMP_NS_DC, 2095 "description", 2096 iptc.fDescription, 2097 options); 2098 2099 SyncString (XMP_NS_PHOTOSHOP, 2100 "CaptionWriter", 2101 iptc.fDescriptionWriter, 2102 options); 2103 2104 } 2105 2106 /*****************************************************************************/ 2107 2108 void dng_xmp::IngestIPTC (dng_metadata &metadata, 2109 bool xmpIsNewer) 2110 { 2111 2112 if (metadata.IPTCLength ()) 2113 { 2114 2115 // Parse the IPTC block. 2116 2117 dng_iptc iptc; 2118 2119 iptc.Parse (metadata.IPTCData (), 2120 metadata.IPTCLength (), 2121 metadata.IPTCOffset ()); 2122 2123 // Compute fingerprint of IPTC data both ways, including and 2124 // excluding the padding data. 2125 2126 dng_fingerprint iptcDigest1 = metadata.IPTCDigest (true ); 2127 dng_fingerprint iptcDigest2 = metadata.IPTCDigest (false); 2128 2129 // See if there is an IPTC fingerprint stored in the XMP. 2130 2131 dng_fingerprint xmpDigest = GetIPTCDigest (); 2132 2133 if (xmpDigest.IsValid ()) 2134 { 2135 2136 // If they match, the XMP was already synced with this 2137 // IPTC block, and we should not resync since it might 2138 // overwrite changes in the XMP data. 2139 2140 if (iptcDigest1 == xmpDigest) 2141 { 2142 2143 return; 2144 2145 } 2146 2147 // If it matches the incorrectly computed digest, skip 2148 // the sync, but fix the digest in the XMP. 2149 2150 if (iptcDigest2 == xmpDigest) 2151 { 2152 2153 SetIPTCDigest (iptcDigest1); 2154 2155 return; 2156 2157 } 2158 2159 // Else the IPTC has changed, so force an update. 2160 2161 xmpIsNewer = false; 2162 2163 } 2164 2165 else 2166 { 2167 2168 // There is no IPTC digest. Previously we would 2169 // prefer the IPTC in this case, but the MWG suggests 2170 // that we prefer the XMP in this case. 2171 2172 xmpIsNewer = true; 2173 2174 } 2175 2176 // Remember the fingerprint of the IPTC we are syncing with. 2177 2178 SetIPTCDigest (iptcDigest1); 2179 2180 // Find the sync options. 2181 2182 uint32 options = xmpIsNewer ? preferXMP 2183 : preferNonXMP; 2184 2185 // Synchronize the fields. 2186 2187 SyncIPTC (iptc, options); 2188 2189 } 2190 2191 // After the IPTC data is moved to XMP, we don't need it anymore. 2192 2193 metadata.ClearIPTC (); 2194 2195 } 2196 2197 /*****************************************************************************/ 2198 2199 void dng_xmp::RebuildIPTC (dng_metadata &metadata, 2200 dng_memory_allocator &allocator, 2201 bool padForTIFF) 2202 { 2203 2204 // If there is no XMP, then there is no IPTC. 2205 2206 if (!fSDK->HasMeta ()) 2207 { 2208 return; 2209 } 2210 2211 // Extract the legacy IPTC fields from the XMP data. 2212 2213 dng_iptc iptc; 2214 2215 SyncIPTC (iptc, preferXMP); 2216 2217 // Build legacy IPTC record 2218 2219 if (iptc.NotEmpty ()) 2220 { 2221 2222 AutoPtr<dng_memory_block> block (iptc.Spool (allocator, 2223 padForTIFF)); 2224 2225 metadata.SetIPTC (block); 2226 2227 } 2228 2229 } 2230 2231 /*****************************************************************************/ 2232 2233 void dng_xmp::SyncFlash (uint32 &flashState, 2234 uint32 &flashMask, 2235 uint32 options) 2236 { 2237 2238 bool isDefault = (flashState == 0xFFFFFFFF); 2239 2240 if ((options & ignoreXMP) || !isDefault) 2241 { 2242 2243 Remove (XMP_NS_EXIF, "Flash"); 2244 2245 } 2246 2247 if (!isDefault) 2248 { 2249 2250 fSDK->SetStructField (XMP_NS_EXIF, 2251 "Flash", 2252 XMP_NS_EXIF, 2253 "Fired", 2254 (flashState & 0x1) ? "True" : "False"); 2255 2256 if (((flashMask >> 1) & 3) == 3) 2257 { 2258 2259 char s [8]; 2260 2261 sprintf (s, "%u", (unsigned) ((flashState >> 1) & 3)); 2262 2263 fSDK->SetStructField (XMP_NS_EXIF, 2264 "Flash", 2265 XMP_NS_EXIF, 2266 "Return", 2267 s); 2268 2269 } 2270 2271 if (((flashMask >> 3) & 3) == 3) 2272 { 2273 2274 char s [8]; 2275 2276 sprintf (s, "%u", (unsigned) ((flashState >> 3) & 3)); 2277 2278 fSDK->SetStructField (XMP_NS_EXIF, 2279 "Flash", 2280 XMP_NS_EXIF, 2281 "Mode", 2282 s); 2283 2284 } 2285 2286 if ((flashMask & (1 << 5)) != 0) 2287 { 2288 2289 fSDK->SetStructField (XMP_NS_EXIF, 2290 "Flash", 2291 XMP_NS_EXIF, 2292 "Function", 2293 (flashState & (1 << 5)) ? "True" : "False"); 2294 2295 } 2296 2297 if ((flashMask & (1 << 6)) != 0) 2298 { 2299 2300 fSDK->SetStructField (XMP_NS_EXIF, 2301 "Flash", 2302 XMP_NS_EXIF, 2303 "RedEyeMode", 2304 (flashState & (1 << 6)) ? "True" : "False"); 2305 2306 } 2307 2308 } 2309 2310 else if (fSDK->Exists (XMP_NS_EXIF, "Flash")) 2311 { 2312 2313 dng_string s; 2314 2315 if (fSDK->GetStructField (XMP_NS_EXIF, 2316 "Flash", 2317 XMP_NS_EXIF, 2318 "Fired", 2319 s)) 2320 { 2321 2322 flashState = 0; 2323 flashMask = 1; 2324 2325 if (s.Matches ("True")) 2326 { 2327 flashState |= 1; 2328 } 2329 2330 if (fSDK->GetStructField (XMP_NS_EXIF, 2331 "Flash", 2332 XMP_NS_EXIF, 2333 "Return", 2334 s)) 2335 { 2336 2337 unsigned x = 0; 2338 2339 if (sscanf (s.Get (), "%u", &x) == 1 && x <= 3) 2340 { 2341 2342 flashState |= x << 1; 2343 flashMask |= 3 << 1; 2344 2345 } 2346 2347 } 2348 2349 if (fSDK->GetStructField (XMP_NS_EXIF, 2350 "Flash", 2351 XMP_NS_EXIF, 2352 "Mode", 2353 s)) 2354 { 2355 2356 unsigned x = 0; 2357 2358 if (sscanf (s.Get (), "%u", &x) == 1 && x <= 3) 2359 { 2360 2361 flashState |= x << 3; 2362 flashMask |= 3 << 3; 2363 2364 } 2365 2366 } 2367 2368 if (fSDK->GetStructField (XMP_NS_EXIF, 2369 "Flash", 2370 XMP_NS_EXIF, 2371 "Function", 2372 s)) 2373 { 2374 2375 flashMask |= 1 << 5; 2376 2377 if (s.Matches ("True")) 2378 { 2379 flashState |= 1 << 5; 2380 } 2381 2382 } 2383 2384 if (fSDK->GetStructField (XMP_NS_EXIF, 2385 "Flash", 2386 XMP_NS_EXIF, 2387 "RedEyeMode", 2388 s)) 2389 { 2390 2391 flashMask |= 1 << 6; 2392 2393 if (s.Matches ("True")) 2394 { 2395 flashState |= 1 << 6; 2396 } 2397 2398 } 2399 2400 } 2401 2402 } 2403 2404 } 2405 2406 /*****************************************************************************/ 2407 2408 void dng_xmp::GenerateDefaultLensName (dng_exif &exif) 2409 { 2410 2411 // Generate default lens name from lens info if required. 2412 // Ignore names names that end in "f/0.0" due to third party bug. 2413 2414 if ((exif.fLensName.IsEmpty () || 2415 exif.fLensName.EndsWith ("f/0.0")) && exif.fLensInfo [0].IsValid ()) 2416 { 2417 2418 char s [256]; 2419 2420 real64 minFL = exif.fLensInfo [0].As_real64 (); 2421 real64 maxFL = exif.fLensInfo [1].As_real64 (); 2422 2423 // The f-stop numbers are optional. 2424 2425 if (exif.fLensInfo [2].IsValid ()) 2426 { 2427 2428 real64 minFS = exif.fLensInfo [2].As_real64 (); 2429 real64 maxFS = exif.fLensInfo [3].As_real64 (); 2430 2431 if (minFL == maxFL) 2432 sprintf (s, "%.1f mm f/%.1f", minFL, minFS); 2433 2434 else if (minFS == maxFS) 2435 sprintf (s, "%.1f-%.1f mm f/%.1f", minFL, maxFL, minFS); 2436 2437 else 2438 sprintf (s, "%.1f-%.1f mm f/%.1f-%.1f", minFL, maxFL, minFS, maxFS); 2439 2440 } 2441 2442 else 2443 { 2444 2445 if (minFL == maxFL) 2446 sprintf (s, "%.1f mm", minFL); 2447 2448 else 2449 sprintf (s, "%.1f-%.1f mm", minFL, maxFL); 2450 2451 } 2452 2453 exif.fLensName.Set (s); 2454 2455 SetString (XMP_NS_AUX, 2456 "Lens", 2457 exif.fLensName); 2458 2459 // Don't generate exifEX for now. 2460 2461 // SetString (XMP_NS_EXIFEX, 2462 // "LensModel", 2463 // exif.fLensName); 2464 2465 } 2466 2467 } 2468 2469 /*****************************************************************************/ 2470 2471 void dng_xmp::SyncLensName (dng_exif &exif) 2472 { 2473 2474 // EXIF lens names are sometimes missing or wrong (esp. when non-OEM lenses 2475 // are used). So prefer the value from XMP. 2476 2477 // Check XMP for the lens model in the aux namespace first. If not there, 2478 // then check the exifEX namespace. 2479 2480 if (!SyncString (XMP_NS_AUX, 2481 "Lens", 2482 exif.fLensName, 2483 preferXMP)) 2484 { 2485 2486 SyncString (XMP_NS_EXIFEX, 2487 "LensModel", 2488 exif.fLensName, 2489 preferXMP); 2490 2491 } 2492 2493 GenerateDefaultLensName (exif); 2494 2495 } 2496 2497 /*****************************************************************************/ 2498 2499 void dng_xmp::SyncExif (dng_exif &exif, 2500 const dng_exif *originalExif, 2501 bool doingUpdateFromXMP, 2502 bool removeFromXMP) 2503 { 2504 2505 DNG_ASSERT (!doingUpdateFromXMP || originalExif, 2506 "Must have original EXIF if doingUpdateFromXMP"); 2507 2508 // Default synchronization options for the read-only fields. 2509 2510 uint32 readOnly = doingUpdateFromXMP ? ignoreXMP 2511 : preferNonXMP; 2512 2513 // Option for removable fields. 2514 2515 uint32 removable = removeFromXMP ? removeXMP 2516 : 0; 2517 2518 // Make: 2519 2520 SyncString (XMP_NS_TIFF, 2521 "Make", 2522 exif.fMake, 2523 readOnly + removable); 2524 2525 // Model: 2526 2527 SyncString (XMP_NS_TIFF, 2528 "Model", 2529 exif.fModel, 2530 readOnly + removable); 2531 2532 // Exif version number: 2533 2534 { 2535 2536 // Find version number in XMP, if any. 2537 2538 uint32 xmpVersion = 0; 2539 2540 { 2541 2542 dng_string s; 2543 2544 if (GetString (XMP_NS_EXIF, "ExifVersion", s)) 2545 { 2546 2547 unsigned b0; 2548 unsigned b1; 2549 unsigned b2; 2550 unsigned b3; 2551 2552 if (sscanf (s.Get (), 2553 "%1u%1u%1u%1u", 2554 &b0, 2555 &b1, 2556 &b2, 2557 &b3) == 4) 2558 { 2559 2560 if (b0 <= 9 && b1 <= 9 && b2 <= 9 && b3 <= 9) 2561 { 2562 2563 b0 += '0'; 2564 b1 += '0'; 2565 b2 += '0'; 2566 b3 += '0'; 2567 2568 xmpVersion = (b0 << 24) | 2569 (b1 << 16) | 2570 (b2 << 8) | 2571 (b3 ); 2572 2573 } 2574 2575 } 2576 2577 } 2578 2579 } 2580 2581 // Use maximum logic for merging. 2582 2583 exif.fExifVersion = Max_uint32 (exif.fExifVersion, xmpVersion); 2584 2585 // Provide default value for ExifVersion. 2586 2587 if (!exif.fExifVersion) 2588 { 2589 exif.SetVersion0231 (); 2590 } 2591 2592 // Update XMP. 2593 2594 dng_string xmpString; 2595 2596 if (exif.fExifVersion) 2597 { 2598 2599 unsigned b0 = ((exif.fExifVersion >> 24) & 0x0FF) - '0'; 2600 unsigned b1 = ((exif.fExifVersion >> 16) & 0x0FF) - '0'; 2601 unsigned b2 = ((exif.fExifVersion >> 8) & 0x0FF) - '0'; 2602 unsigned b3 = ((exif.fExifVersion ) & 0x0FF) - '0'; 2603 2604 if (b0 <= 9 && b1 <= 9 && b2 <= 9 && b3 <= 9) 2605 { 2606 2607 char s [5]; 2608 2609 sprintf (s, 2610 "%1u%1u%1u%1u", 2611 b0, 2612 b1, 2613 b2, 2614 b3); 2615 2616 xmpString.Set (s); 2617 2618 } 2619 2620 } 2621 2622 if (removeFromXMP || xmpString.IsEmpty ()) 2623 { 2624 2625 Remove (XMP_NS_EXIF, "ExifVersion"); 2626 2627 } 2628 2629 else 2630 { 2631 2632 SetString (XMP_NS_EXIF, "ExifVersion", xmpString); 2633 2634 } 2635 2636 } 2637 2638 // ExposureTime / ShutterSpeedValue: 2639 2640 { 2641 2642 // Process twice in case XMP contains only one of the 2643 // two fields. 2644 2645 for (uint32 pass = 0; pass < 2; pass++) 2646 { 2647 2648 dng_urational et = exif.fExposureTime; 2649 2650 Sync_urational (XMP_NS_EXIF, 2651 "ExposureTime", 2652 et, 2653 readOnly); 2654 2655 if (et.IsValid ()) 2656 { 2657 2658 exif.SetExposureTime (et.As_real64 (), false); 2659 2660 } 2661 2662 dng_srational ss = exif.fShutterSpeedValue; 2663 2664 Sync_srational (XMP_NS_EXIF, 2665 "ShutterSpeedValue", 2666 ss, 2667 readOnly); 2668 2669 if (ss.IsValid ()) 2670 { 2671 2672 exif.SetShutterSpeedValue (ss.As_real64 ()); 2673 2674 } 2675 2676 } 2677 2678 if (removeFromXMP) 2679 { 2680 2681 Remove (XMP_NS_EXIF, "ExposureTime"); 2682 2683 Remove (XMP_NS_EXIF, "ShutterSpeedValue"); 2684 2685 } 2686 2687 } 2688 2689 // FNumber / ApertureValue: 2690 2691 { 2692 2693 for (uint32 pass = 0; pass < 2; pass++) 2694 { 2695 2696 dng_urational fs = exif.fFNumber; 2697 2698 Sync_urational (XMP_NS_EXIF, 2699 "FNumber", 2700 fs, 2701 readOnly); 2702 2703 if (fs.IsValid ()) 2704 { 2705 2706 exif.SetFNumber (fs.As_real64 ()); 2707 2708 } 2709 2710 dng_urational av = exif.fApertureValue; 2711 2712 Sync_urational (XMP_NS_EXIF, 2713 "ApertureValue", 2714 av, 2715 readOnly); 2716 2717 if (av.IsValid ()) 2718 { 2719 2720 exif.SetApertureValue (av.As_real64 ()); 2721 2722 } 2723 2724 } 2725 2726 if (removeFromXMP) 2727 { 2728 2729 Remove (XMP_NS_EXIF, "FNumber"); 2730 2731 Remove (XMP_NS_EXIF, "ApertureValue"); 2732 2733 } 2734 2735 } 2736 2737 // Exposure program: 2738 2739 Sync_uint32 (XMP_NS_EXIF, 2740 "ExposureProgram", 2741 exif.fExposureProgram, 2742 exif.fExposureProgram == 0xFFFFFFFF, 2743 readOnly + removable); 2744 2745 // ISO Speed Ratings: 2746 2747 { 2748 2749 uint32 isoSpeedRatingsCount = 0; 2750 2751 uint32 isoSpeedRatingsOptions = readOnly; 2752 2753 uint32 oldISOSpeedRatings [3]; 2754 2755 memcpy (oldISOSpeedRatings, 2756 exif.fISOSpeedRatings, 2757 sizeof (oldISOSpeedRatings)); 2758 2759 bool checkXMPForHigherISO = false; 2760 2761 for (uint32 j = 0; j < 3; j++) 2762 { 2763 2764 // Special case: the EXIF 2.2x standard represents ISO speed ratings with 2765 // 2 bytes, which cannot hold ISO speed ratings above 65535 (e.g., 2766 // 102400). If the EXIF ISO speed rating value is 65535, prefer the XMP 2767 // ISOSpeedRatings tag value. 2768 2769 if (exif.fISOSpeedRatings [j] == 65535) 2770 { 2771 2772 isoSpeedRatingsOptions = preferXMP; 2773 2774 checkXMPForHigherISO = true; 2775 2776 isoSpeedRatingsCount = 0; 2777 2778 break; 2779 2780 } 2781 2782 else if (exif.fISOSpeedRatings [j] == 0) 2783 { 2784 break; 2785 } 2786 2787 isoSpeedRatingsCount++; 2788 2789 } 2790 2791 Sync_uint32_array (XMP_NS_EXIF, 2792 "ISOSpeedRatings", 2793 exif.fISOSpeedRatings, 2794 isoSpeedRatingsCount, 2795 3, 2796 isoSpeedRatingsOptions); 2797 2798 // If the EXIF ISO was 65535 and we failed to find anything meaningful in the 2799 // XMP, then we fall back to the EXIF ISO. 2800 2801 if (checkXMPForHigherISO && (isoSpeedRatingsCount == 0)) 2802 { 2803 2804 memcpy (exif.fISOSpeedRatings, 2805 oldISOSpeedRatings, 2806 sizeof (oldISOSpeedRatings)); 2807 2808 } 2809 2810 // Only remove the ISO tag if there are not ratings over 65535. 2811 2812 if (removeFromXMP) 2813 { 2814 2815 bool hasHighISO = false; 2816 2817 for (uint32 j = 0; j < 3; j++) 2818 { 2819 2820 if (exif.fISOSpeedRatings [j] == 0) 2821 { 2822 break; 2823 } 2824 2825 hasHighISO = hasHighISO || (exif.fISOSpeedRatings [j] > 65535); 2826 2827 } 2828 2829 if (!hasHighISO) 2830 { 2831 2832 Remove (XMP_NS_EXIF, "ISOSpeedRatings"); 2833 2834 } 2835 2836 } 2837 2838 } 2839 2840 // SensitivityType: 2841 2842 Sync_uint32 (XMP_NS_EXIF, 2843 "SensitivityType", 2844 exif.fSensitivityType, 2845 exif.fSensitivityType == stUnknown, 2846 readOnly + removable); 2847 2848 // StandardOutputSensitivity: 2849 2850 Sync_uint32 (XMP_NS_EXIF, 2851 "StandardOutputSensitivity", 2852 exif.fStandardOutputSensitivity, 2853 exif.fStandardOutputSensitivity == 0, 2854 readOnly + removable); 2855 2856 // RecommendedExposureIndex: 2857 2858 Sync_uint32 (XMP_NS_EXIF, 2859 "RecommendedExposureIndex", 2860 exif.fRecommendedExposureIndex, 2861 exif.fRecommendedExposureIndex == 0, 2862 readOnly + removable); 2863 2864 // ISOSpeed: 2865 2866 Sync_uint32 (XMP_NS_EXIF, 2867 "ISOSpeed", 2868 exif.fISOSpeed, 2869 exif.fISOSpeed == 0, 2870 readOnly + removable); 2871 2872 // ISOSpeedLatitudeyyy: 2873 2874 Sync_uint32 (XMP_NS_EXIF, 2875 "ISOSpeedLatitudeyyy", 2876 exif.fISOSpeedLatitudeyyy, 2877 exif.fISOSpeedLatitudeyyy == 0, 2878 readOnly + removable); 2879 2880 // ISOSpeedLatitudezzz: 2881 2882 Sync_uint32 (XMP_NS_EXIF, 2883 "ISOSpeedLatitudezzz", 2884 exif.fISOSpeedLatitudezzz, 2885 exif.fISOSpeedLatitudezzz == 0, 2886 readOnly + removable); 2887 2888 // ExposureIndex: 2889 2890 Sync_urational (XMP_NS_EXIF, 2891 "ExposureIndex", 2892 exif.fExposureIndex, 2893 readOnly + removable); 2894 2895 // Brightness Value: 2896 2897 Sync_srational (XMP_NS_EXIF, 2898 "BrightnessValue", 2899 exif.fBrightnessValue, 2900 readOnly + removable); 2901 2902 // Exposure Bias: 2903 2904 Sync_srational (XMP_NS_EXIF, 2905 "ExposureBiasValue", 2906 exif.fExposureBiasValue, 2907 readOnly + removable); 2908 2909 // Max Aperture: 2910 2911 Sync_urational (XMP_NS_EXIF, 2912 "MaxApertureValue", 2913 exif.fMaxApertureValue, 2914 readOnly + removable); 2915 2916 // Subject Distance: 2917 2918 Sync_urational (XMP_NS_EXIF, 2919 "SubjectDistance", 2920 exif.fSubjectDistance, 2921 readOnly + removable); 2922 2923 // Metering Mode: 2924 2925 Sync_uint32 (XMP_NS_EXIF, 2926 "MeteringMode", 2927 exif.fMeteringMode, 2928 exif.fMeteringMode == 0xFFFFFFFF, 2929 readOnly + removable); 2930 2931 // Light Source: 2932 2933 Sync_uint32 (XMP_NS_EXIF, 2934 "LightSource", 2935 exif.fLightSource, 2936 exif.fLightSource > 0x0FFFF, 2937 readOnly + removable); 2938 2939 // Flash State: 2940 2941 SyncFlash (exif.fFlash, 2942 exif.fFlashMask, 2943 readOnly); 2944 2945 if (removeFromXMP) 2946 { 2947 Remove (XMP_NS_EXIF, "Flash"); 2948 } 2949 2950 // Focal Length: 2951 2952 Sync_urational (XMP_NS_EXIF, 2953 "FocalLength", 2954 exif.fFocalLength, 2955 readOnly + removable); 2956 2957 // Sensing Method. 2958 2959 Sync_uint32 (XMP_NS_EXIF, 2960 "SensingMethod", 2961 exif.fSensingMethod, 2962 exif.fSensingMethod > 0x0FFFF, 2963 readOnly + removable); 2964 2965 // File Source. 2966 2967 Sync_uint32 (XMP_NS_EXIF, 2968 "FileSource", 2969 exif.fFileSource, 2970 exif.fFileSource > 0x0FF, 2971 readOnly + removable); 2972 2973 // Scene Type. 2974 2975 Sync_uint32 (XMP_NS_EXIF, 2976 "SceneType", 2977 exif.fSceneType, 2978 exif.fSceneType > 0x0FF, 2979 readOnly + removable); 2980 2981 // Focal Length in 35mm Film: 2982 2983 Sync_uint32 (XMP_NS_EXIF, 2984 "FocalLengthIn35mmFilm", 2985 exif.fFocalLengthIn35mmFilm, 2986 exif.fFocalLengthIn35mmFilm == 0, 2987 readOnly + removable); 2988 2989 // Custom Rendered: 2990 2991 Sync_uint32 (XMP_NS_EXIF, 2992 "CustomRendered", 2993 exif.fCustomRendered, 2994 exif.fCustomRendered > 0x0FFFF, 2995 readOnly + removable); 2996 2997 // Exposure Mode: 2998 2999 Sync_uint32 (XMP_NS_EXIF, 3000 "ExposureMode", 3001 exif.fExposureMode, 3002 exif.fExposureMode > 0x0FFFF, 3003 readOnly + removable); 3004 3005 // White Balance: 3006 3007 Sync_uint32 (XMP_NS_EXIF, 3008 "WhiteBalance", 3009 exif.fWhiteBalance, 3010 exif.fWhiteBalance > 0x0FFFF, 3011 readOnly + removable); 3012 3013 // Scene Capture Type: 3014 3015 Sync_uint32 (XMP_NS_EXIF, 3016 "SceneCaptureType", 3017 exif.fSceneCaptureType, 3018 exif.fSceneCaptureType > 0x0FFFF, 3019 readOnly + removable); 3020 3021 // Gain Control: 3022 3023 Sync_uint32 (XMP_NS_EXIF, 3024 "GainControl", 3025 exif.fGainControl, 3026 exif.fGainControl > 0x0FFFF, 3027 readOnly + removable); 3028 3029 // Contrast: 3030 3031 Sync_uint32 (XMP_NS_EXIF, 3032 "Contrast", 3033 exif.fContrast, 3034 exif.fContrast > 0x0FFFF, 3035 readOnly + removable); 3036 3037 // Saturation: 3038 3039 Sync_uint32 (XMP_NS_EXIF, 3040 "Saturation", 3041 exif.fSaturation, 3042 exif.fSaturation > 0x0FFFF, 3043 readOnly + removable); 3044 3045 // Sharpness: 3046 3047 Sync_uint32 (XMP_NS_EXIF, 3048 "Sharpness", 3049 exif.fSharpness, 3050 exif.fSharpness > 0x0FFFF, 3051 readOnly + removable); 3052 3053 // Subject Distance Range: 3054 3055 Sync_uint32 (XMP_NS_EXIF, 3056 "SubjectDistanceRange", 3057 exif.fSubjectDistanceRange, 3058 exif.fSubjectDistanceRange > 0x0FFFF, 3059 readOnly + removable); 3060 3061 // Subject Area: 3062 3063 Sync_uint32_array (XMP_NS_EXIF, 3064 "SubjectArea", 3065 exif.fSubjectArea, 3066 exif.fSubjectAreaCount, 3067 sizeof (exif.fSubjectArea ) / 3068 sizeof (exif.fSubjectArea [0]), 3069 readOnly); 3070 3071 if (removeFromXMP) 3072 { 3073 Remove (XMP_NS_EXIF, "SubjectArea"); 3074 } 3075 3076 // Digital Zoom Ratio: 3077 3078 Sync_urational (XMP_NS_EXIF, 3079 "DigitalZoomRatio", 3080 exif.fDigitalZoomRatio, 3081 readOnly + removable); 3082 3083 // Focal Plane Resolution: 3084 3085 Sync_urational (XMP_NS_EXIF, 3086 "FocalPlaneXResolution", 3087 exif.fFocalPlaneXResolution, 3088 readOnly + removable); 3089 3090 Sync_urational (XMP_NS_EXIF, 3091 "FocalPlaneYResolution", 3092 exif.fFocalPlaneYResolution, 3093 readOnly + removable); 3094 3095 Sync_uint32 (XMP_NS_EXIF, 3096 "FocalPlaneResolutionUnit", 3097 exif.fFocalPlaneResolutionUnit, 3098 exif.fFocalPlaneResolutionUnit > 0x0FFFF, 3099 readOnly + removable); 3100 3101 // ImageDescription: (XMP is is always preferred) 3102 3103 if (fSDK->GetAltLangDefault (XMP_NS_DC, 3104 "description", 3105 exif.fImageDescription)) 3106 3107 { 3108 3109 } 3110 3111 else if (doingUpdateFromXMP) 3112 { 3113 3114 exif.fImageDescription.Clear (); 3115 3116 if (originalExif->fImageDescription.NotEmpty ()) 3117 { 3118 3119 fSDK->SetAltLangDefault (XMP_NS_DC, 3120 "description", 3121 dng_string ()); 3122 3123 } 3124 3125 } 3126 3127 else if (exif.fImageDescription.NotEmpty ()) 3128 { 3129 3130 fSDK->SetAltLangDefault (XMP_NS_DC, 3131 "description", 3132 exif.fImageDescription); 3133 3134 } 3135 3136 // Artist: (XMP is is always preferred) 3137 3138 { 3139 3140 dng_string_list xmpList; 3141 3142 if (fSDK->GetStringList (XMP_NS_DC, 3143 "creator", 3144 xmpList)) 3145 { 3146 3147 exif.fArtist.Clear (); 3148 3149 if (xmpList.Count () > 0) 3150 { 3151 3152 uint32 j; 3153 3154 uint32 bufferSize = xmpList.Count () * 4 + 1; 3155 3156 for (j = 0; j < xmpList.Count (); j++) 3157 { 3158 3159 bufferSize += xmpList [j].Length () * 2; 3160 3161 } 3162 3163 dng_memory_data temp (bufferSize); 3164 3165 char *t = temp.Buffer_char (); 3166 3167 for (j = 0; j < xmpList.Count (); j++) 3168 { 3169 3170 const char *s = xmpList [j].Get (); 3171 3172 bool needQuotes = xmpList [j].Contains ("; ") || 3173 s [0] == '\"'; 3174 3175 if (needQuotes) 3176 { 3177 *(t++) = '\"'; 3178 } 3179 3180 while (s [0] != 0) 3181 { 3182 3183 if (s [0] == '\"' && needQuotes) 3184 { 3185 *(t++) = '\"'; 3186 } 3187 3188 *(t++) = *(s++); 3189 3190 } 3191 3192 if (needQuotes) 3193 { 3194 *(t++) = '\"'; 3195 } 3196 3197 if (j != xmpList.Count () - 1) 3198 { 3199 *(t++) = ';'; 3200 *(t++) = ' '; 3201 } 3202 else 3203 { 3204 *t = 0; 3205 } 3206 3207 } 3208 3209 exif.fArtist.Set (temp.Buffer_char ()); 3210 3211 } 3212 3213 } 3214 3215 else if (doingUpdateFromXMP) 3216 { 3217 3218 exif.fArtist.Clear (); 3219 3220 if (originalExif->fArtist.NotEmpty ()) 3221 { 3222 3223 dng_string_list fakeList; 3224 3225 fakeList.Append (dng_string ()); 3226 3227 SetStringList (XMP_NS_DC, 3228 "creator", 3229 fakeList, 3230 false); 3231 3232 } 3233 3234 } 3235 3236 else if (exif.fArtist.NotEmpty ()) 3237 { 3238 3239 dng_string_list newList; 3240 3241 dng_memory_data temp (exif.fArtist.Length () + 1); 3242 3243 const char *s = exif.fArtist.Get (); 3244 3245 char *t = temp.Buffer_char (); 3246 3247 bool first = true; 3248 3249 bool quoted = false; 3250 3251 bool valid = true; 3252 3253 while (s [0] != 0 && valid) 3254 { 3255 3256 if (first) 3257 { 3258 3259 if (s [0] == '\"') 3260 { 3261 3262 quoted = true; 3263 3264 s++; 3265 3266 } 3267 3268 } 3269 3270 first = false; 3271 3272 if (quoted) 3273 { 3274 3275 if (s [0] == '\"' && 3276 s [1] == '\"') 3277 { 3278 3279 s+= 2; 3280 3281 *(t++) = '\"'; 3282 3283 } 3284 3285 else if (s [0] == '\"') 3286 { 3287 3288 s++; 3289 3290 quoted = false; 3291 3292 valid = valid && ((s [0] == 0) || ((s [0] == ';' && s [1] == ' '))); 3293 3294 } 3295 3296 else 3297 { 3298 3299 *(t++) = *(s++); 3300 3301 } 3302 3303 } 3304 3305 else if (s [0] == ';' && 3306 s [1] == ' ') 3307 { 3308 3309 s += 2; 3310 3311 t [0] = 0; 3312 3313 dng_string ss; 3314 3315 ss.Set (temp.Buffer_char ()); 3316 3317 newList.Append (ss); 3318 3319 t = temp.Buffer_char (); 3320 3321 first = true; 3322 3323 } 3324 3325 else 3326 { 3327 3328 *(t++) = *(s++); 3329 3330 } 3331 3332 } 3333 3334 if (quoted) 3335 { 3336 3337 valid = false; 3338 3339 } 3340 3341 if (valid) 3342 { 3343 3344 if (t != temp.Buffer_char ()) 3345 { 3346 3347 t [0] = 0; 3348 3349 dng_string ss; 3350 3351 ss.Set (temp.Buffer_char ()); 3352 3353 newList.Append (ss); 3354 3355 } 3356 3357 } 3358 3359 else 3360 { 3361 3362 newList.Clear (); 3363 3364 newList.Append (exif.fArtist); 3365 3366 } 3367 3368 SetStringList (XMP_NS_DC, 3369 "creator", 3370 newList, 3371 false); 3372 3373 } 3374 3375 } 3376 3377 // Software: (XMP is is always preferred) 3378 3379 if (fSDK->GetString (XMP_NS_XAP, 3380 "CreatorTool", 3381 exif.fSoftware)) 3382 3383 { 3384 3385 } 3386 3387 else if (doingUpdateFromXMP) 3388 { 3389 3390 exif.fSoftware.Clear (); 3391 3392 if (originalExif->fSoftware.NotEmpty ()) 3393 { 3394 3395 fSDK->SetString (XMP_NS_XAP, 3396 "CreatorTool", 3397 dng_string ()); 3398 3399 } 3400 3401 } 3402 3403 else if (exif.fSoftware.NotEmpty ()) 3404 { 3405 3406 fSDK->SetString (XMP_NS_XAP, 3407 "CreatorTool", 3408 exif.fSoftware); 3409 3410 } 3411 3412 // Copyright: (XMP is is always preferred) 3413 3414 if (fSDK->GetAltLangDefault (XMP_NS_DC, 3415 "rights", 3416 exif.fCopyright)) 3417 3418 { 3419 3420 } 3421 3422 else if (doingUpdateFromXMP) 3423 { 3424 3425 exif.fCopyright.Clear (); 3426 3427 if (originalExif->fCopyright.NotEmpty ()) 3428 { 3429 3430 fSDK->SetAltLangDefault (XMP_NS_DC, 3431 "rights", 3432 dng_string ()); 3433 3434 } 3435 3436 } 3437 3438 else if (exif.fCopyright.NotEmpty ()) 3439 { 3440 3441 fSDK->SetAltLangDefault (XMP_NS_DC, 3442 "rights", 3443 exif.fCopyright); 3444 3445 } 3446 3447 // Camera serial number private tag: 3448 3449 SyncString (XMP_NS_AUX, 3450 "SerialNumber", 3451 exif.fCameraSerialNumber, 3452 readOnly); 3453 3454 // Lens Info: 3455 3456 { 3457 3458 dng_string s; 3459 3460 if (exif.fLensInfo [0].IsValid ()) 3461 { 3462 3463 char ss [256]; 3464 3465 sprintf (ss, 3466 "%u/%u %u/%u %u/%u %u/%u", 3467 (unsigned) exif.fLensInfo [0].n, 3468 (unsigned) exif.fLensInfo [0].d, 3469 (unsigned) exif.fLensInfo [1].n, 3470 (unsigned) exif.fLensInfo [1].d, 3471 (unsigned) exif.fLensInfo [2].n, 3472 (unsigned) exif.fLensInfo [2].d, 3473 (unsigned) exif.fLensInfo [3].n, 3474 (unsigned) exif.fLensInfo [3].d); 3475 3476 s.Set (ss); 3477 3478 } 3479 3480 // Check XMP for the lens specification in the aux namespace first. If 3481 // not there, then check the exifEX namespace. 3482 3483 SyncString (XMP_NS_AUX, 3484 "LensInfo", 3485 s, 3486 readOnly); 3487 3488 if (s.NotEmpty ()) 3489 { 3490 3491 unsigned n [4]; 3492 unsigned d [4]; 3493 3494 if (sscanf (s.Get (), 3495 "%u/%u %u/%u %u/%u %u/%u", 3496 &n [0], 3497 &d [0], 3498 &n [1], 3499 &d [1], 3500 &n [2], 3501 &d [2], 3502 &n [3], 3503 &d [3]) == 8) 3504 { 3505 3506 for (uint32 j = 0; j < 4; j++) 3507 { 3508 3509 exif.fLensInfo [j] = dng_urational (n [j], d [j]); 3510 3511 } 3512 3513 } 3514 3515 3516 } 3517 3518 else 3519 { 3520 3521 // Not found in aux, so examine exifEX. 3522 3523 dng_string_list strList; 3524 3525 SyncStringList (XMP_NS_EXIFEX, 3526 "LensSpecification", 3527 strList, 3528 false, 3529 readOnly); 3530 3531 if (strList.Count () == 4) 3532 { 3533 3534 const dng_string &s0 = strList [0]; 3535 const dng_string &s1 = strList [1]; 3536 const dng_string &s2 = strList [2]; 3537 const dng_string &s3 = strList [3]; 3538 3539 unsigned n [4]; 3540 unsigned d [4]; 3541 3542 if (sscanf (s0.Get (), "%u/%u", &n [0], &d [0]) == 2 && 3543 sscanf (s1.Get (), "%u/%u", &n [1], &d [1]) == 2 && 3544 sscanf (s2.Get (), "%u/%u", &n [2], &d [2]) == 2 && 3545 sscanf (s3.Get (), "%u/%u", &n [3], &d [3]) == 2) 3546 { 3547 3548 for (uint32 j = 0; j < 4; j++) 3549 { 3550 3551 exif.fLensInfo [j] = dng_urational (n [j], d [j]); 3552 3553 } 3554 3555 } 3556 3557 } 3558 3559 } 3560 3561 } 3562 3563 // Lens name: 3564 3565 SyncLensName (exif); 3566 3567 // Lens ID: 3568 3569 SyncString (XMP_NS_AUX, 3570 "LensID", 3571 exif.fLensID, 3572 readOnly); 3573 3574 // Lens Make: 3575 3576 if (!SyncString (XMP_NS_EXIF, 3577 "LensMake", 3578 exif.fLensMake, 3579 readOnly + removable)) 3580 3581 { 3582 3583 SyncString (XMP_NS_EXIFEX, 3584 "LensMake", 3585 exif.fLensMake, 3586 readOnly + removable); 3587 3588 } 3589 3590 // Lens Serial Number: 3591 3592 SyncString (XMP_NS_AUX, 3593 "LensSerialNumber", 3594 exif.fLensSerialNumber, 3595 readOnly); 3596 3597 // Image Number: 3598 3599 Sync_uint32 (XMP_NS_AUX, 3600 "ImageNumber", 3601 exif.fImageNumber, 3602 exif.fImageNumber == 0xFFFFFFFF, 3603 preferXMP); // CR-4197237: Preserve aux:ImageNumber in XMP when Updating Metadata 3604 3605 // User Comment: 3606 3607 if (exif.fUserComment.NotEmpty ()) 3608 { 3609 3610 fSDK->SetAltLangDefault (XMP_NS_EXIF, 3611 "UserComment", 3612 exif.fUserComment); 3613 3614 } 3615 3616 else 3617 { 3618 3619 (void) fSDK->GetAltLangDefault (XMP_NS_EXIF, 3620 "UserComment", 3621 exif.fUserComment); 3622 3623 } 3624 3625 if (removeFromXMP) 3626 { 3627 Remove (XMP_NS_EXIF, "UserComment"); 3628 } 3629 3630 // Approximate focus distance: 3631 3632 SyncApproximateFocusDistance (exif, 3633 readOnly); 3634 3635 // LensDistortInfo: 3636 3637 { 3638 3639 dng_string s; 3640 3641 if (exif.HasLensDistortInfo ()) 3642 { 3643 3644 char ss [256]; 3645 3646 sprintf (ss, 3647 "%d/%d %d/%d %d/%d %d/%d", 3648 (int) exif.fLensDistortInfo [0].n, 3649 (int) exif.fLensDistortInfo [0].d, 3650 (int) exif.fLensDistortInfo [1].n, 3651 (int) exif.fLensDistortInfo [1].d, 3652 (int) exif.fLensDistortInfo [2].n, 3653 (int) exif.fLensDistortInfo [2].d, 3654 (int) exif.fLensDistortInfo [3].n, 3655 (int) exif.fLensDistortInfo [3].d); 3656 3657 s.Set (ss); 3658 3659 } 3660 3661 SyncString (XMP_NS_AUX, 3662 "LensDistortInfo", 3663 s, 3664 readOnly); 3665 3666 if (s.NotEmpty ()) 3667 { 3668 3669 int n [4]; 3670 int d [4]; 3671 3672 if (sscanf (s.Get (), 3673 "%d/%d %d/%d %d/%d %d/%d", 3674 &n [0], 3675 &d [0], 3676 &n [1], 3677 &d [1], 3678 &n [2], 3679 &d [2], 3680 &n [3], 3681 &d [3]) == 8) 3682 { 3683 3684 for (uint32 j = 0; j < 4; j++) 3685 { 3686 3687 exif.fLensDistortInfo [j] = dng_srational (n [j], d [j]); 3688 3689 } 3690 3691 } 3692 3693 3694 } 3695 3696 } 3697 3698 // Flash Compensation: 3699 3700 Sync_srational (XMP_NS_AUX, 3701 "FlashCompensation", 3702 exif.fFlashCompensation, 3703 readOnly); 3704 3705 // Owner Name: (allow XMP updates) 3706 3707 SyncString (XMP_NS_AUX, 3708 "OwnerName", 3709 exif.fOwnerName, 3710 preferXMP); 3711 3712 // Firmware: 3713 3714 SyncString (XMP_NS_AUX, 3715 "Firmware", 3716 exif.fFirmware, 3717 readOnly); 3718 3719 // Image Unique ID: 3720 3721 { 3722 3723 dng_string s = EncodeFingerprint (exif.fImageUniqueID); 3724 3725 SyncString (XMP_NS_EXIF, 3726 "ImageUniqueID", 3727 s, 3728 readOnly + removable); 3729 3730 exif.fImageUniqueID = DecodeFingerprint (s); 3731 3732 } 3733 3734 // For the following GPS related fields, we offer an option to prefer XMP to 3735 // the EXIF values. This is to allow the host app to modify the XMP for manual 3736 // geo-tagging and overwrite the EXIF values during export. It also allows the user 3737 // to correct the GPS values via changes in a sidecar XMP file, without modifying 3738 // the original GPS data recorded in the raw file by the camera. 3739 3740 bool preferGPSFromXMP = false; 3741 3742 uint32 gpsSyncOption = preferNonXMP; 3743 3744 #if qDNGPreferGPSMetadataFromXMP 3745 preferGPSFromXMP = true; 3746 #endif 3747 3748 // Allow EXIF GPS to be updated via updates from XMP. 3749 3750 if (doingUpdateFromXMP || preferGPSFromXMP) 3751 { 3752 3753 // Require that at least one basic GPS field exist in the 3754 // XMP before overrriding the EXIF GPS fields. 3755 3756 if (Exists (XMP_NS_EXIF, "GPSVersionID" ) || 3757 Exists (XMP_NS_EXIF, "GPSLatitude" ) || 3758 Exists (XMP_NS_EXIF, "GPSLongitude" ) || 3759 Exists (XMP_NS_EXIF, "GPSAltitude" ) || 3760 Exists (XMP_NS_EXIF, "GPSTimeStamp" ) || 3761 Exists (XMP_NS_EXIF, "GPSProcessingMethod")) 3762 { 3763 3764 // Clear out the GPS info from the EXIF so it will 3765 // replaced by the GPS info from the XMP. 3766 3767 dng_exif blankExif; 3768 3769 exif.CopyGPSFrom (blankExif); 3770 3771 if (preferGPSFromXMP) 3772 { 3773 3774 gpsSyncOption = preferXMP; 3775 3776 } 3777 } 3778 3779 } 3780 3781 // GPS Version ID: 3782 3783 { 3784 3785 dng_string s = EncodeGPSVersion (exif.fGPSVersionID); 3786 3787 if (SyncString (XMP_NS_EXIF, 3788 "GPSVersionID", 3789 s, 3790 gpsSyncOption + removable)) 3791 { 3792 3793 exif.fGPSVersionID = DecodeGPSVersion (s); 3794 3795 } 3796 3797 } 3798 3799 // GPS Latitude: 3800 3801 { 3802 3803 dng_string s = EncodeGPSCoordinate (exif.fGPSLatitudeRef, 3804 exif.fGPSLatitude); 3805 3806 if (SyncString (XMP_NS_EXIF, 3807 "GPSLatitude", 3808 s, 3809 gpsSyncOption + removable)) 3810 { 3811 3812 DecodeGPSCoordinate (s, 3813 exif.fGPSLatitudeRef, 3814 exif.fGPSLatitude); 3815 3816 } 3817 3818 } 3819 3820 // GPS Longitude: 3821 3822 { 3823 3824 dng_string s = EncodeGPSCoordinate (exif.fGPSLongitudeRef, 3825 exif.fGPSLongitude); 3826 3827 if (SyncString (XMP_NS_EXIF, 3828 "GPSLongitude", 3829 s, 3830 gpsSyncOption + removable)) 3831 { 3832 3833 DecodeGPSCoordinate (s, 3834 exif.fGPSLongitudeRef, 3835 exif.fGPSLongitude); 3836 3837 } 3838 3839 } 3840 3841 // Handle simple case of incorrectly written GPS altitude where someone didn't understand the GPSAltitudeRef and assumed the GPSAltitude RATIONAL is signed. 3842 // Only handle this case as we do not want to misinterpret e.g. a fixed point representation of very high GPS altitudes. 3843 3844 uint32 &altitudeRef = exif.fGPSAltitudeRef; 3845 dng_urational &altitude = exif.fGPSAltitude; 3846 3847 if (altitude.IsValid () && 3848 (altitudeRef == 0 || altitudeRef == 0xFFFFFFFF)) // If the file contains a "below sea level" altitudeRef, assume the writing software is working according to the spec. 3849 { 3850 3851 if ((altitude.n & (1U << 31)) && 3852 altitude.d < 7) // As the denominator increases, large numerator values become possibly valid distances. Pick a limit on the conservative side (approx 33e6m) to prevent misinterpretation. 3853 // Noting that the normal case for this mistake has a denominator of 1 3854 { 3855 3856 altitude.n = ~altitude.n + 1; 3857 altitudeRef = 1; 3858 3859 } 3860 3861 } 3862 3863 // GPS Altitude Reference: 3864 3865 Sync_uint32 (XMP_NS_EXIF, 3866 "GPSAltitudeRef", 3867 altitudeRef, 3868 altitudeRef == 0xFFFFFFFF, 3869 gpsSyncOption + removable); 3870 3871 // GPS Altitude: 3872 3873 Sync_urational (XMP_NS_EXIF, 3874 "GPSAltitude", 3875 altitude, 3876 gpsSyncOption + removable); 3877 3878 // GPS Date/Time: 3879 3880 { 3881 3882 dng_string s = EncodeGPSDateTime (exif.fGPSDateStamp, 3883 exif.fGPSTimeStamp); 3884 3885 if (SyncString (XMP_NS_EXIF, 3886 "GPSTimeStamp", 3887 s, 3888 preferNonXMP + removable)) 3889 { 3890 3891 DecodeGPSDateTime (s, 3892 exif.fGPSDateStamp, 3893 exif.fGPSTimeStamp); 3894 3895 } 3896 3897 } 3898 3899 // GPS Satellites: 3900 3901 SyncString (XMP_NS_EXIF, 3902 "GPSSatellites", 3903 exif.fGPSSatellites, 3904 preferNonXMP + removable); 3905 3906 // GPS Status: 3907 3908 SyncString (XMP_NS_EXIF, 3909 "GPSStatus", 3910 exif.fGPSStatus, 3911 preferNonXMP + removable); 3912 3913 // GPS Measure Mode: 3914 3915 SyncString (XMP_NS_EXIF, 3916 "GPSMeasureMode", 3917 exif.fGPSMeasureMode, 3918 preferNonXMP + removable); 3919 3920 // GPS DOP: 3921 3922 Sync_urational (XMP_NS_EXIF, 3923 "GPSDOP", 3924 exif.fGPSDOP, 3925 preferNonXMP + removable); 3926 3927 // GPS Speed Reference: 3928 3929 SyncString (XMP_NS_EXIF, 3930 "GPSSpeedRef", 3931 exif.fGPSSpeedRef, 3932 preferNonXMP + removable); 3933 3934 // GPS Speed: 3935 3936 Sync_urational (XMP_NS_EXIF, 3937 "GPSSpeed", 3938 exif.fGPSSpeed, 3939 preferNonXMP + removable); 3940 3941 // GPS Track Reference: 3942 3943 SyncString (XMP_NS_EXIF, 3944 "GPSTrackRef", 3945 exif.fGPSTrackRef, 3946 preferNonXMP + removable); 3947 3948 // GPS Track: 3949 3950 Sync_urational (XMP_NS_EXIF, 3951 "GPSTrack", 3952 exif.fGPSTrack, 3953 preferNonXMP + removable); 3954 3955 // GPS Image Direction Reference: 3956 3957 SyncString (XMP_NS_EXIF, 3958 "GPSImgDirectionRef", 3959 exif.fGPSImgDirectionRef, 3960 preferNonXMP + removable); 3961 3962 // GPS Image Direction: 3963 3964 Sync_urational (XMP_NS_EXIF, 3965 "GPSImgDirection", 3966 exif.fGPSImgDirection, 3967 preferNonXMP + removable); 3968 3969 // GPS Map Datum: 3970 3971 SyncString (XMP_NS_EXIF, 3972 "GPSMapDatum", 3973 exif.fGPSMapDatum, 3974 preferNonXMP + removable); 3975 3976 // GPS Destination Latitude: 3977 3978 { 3979 3980 dng_string s = EncodeGPSCoordinate (exif.fGPSDestLatitudeRef, 3981 exif.fGPSDestLatitude); 3982 3983 if (SyncString (XMP_NS_EXIF, 3984 "GPSDestLatitude", 3985 s, 3986 preferNonXMP + removable)) 3987 { 3988 3989 DecodeGPSCoordinate (s, 3990 exif.fGPSDestLatitudeRef, 3991 exif.fGPSDestLatitude); 3992 3993 } 3994 3995 } 3996 3997 // GPS Destination Longitude: 3998 3999 { 4000 4001 dng_string s = EncodeGPSCoordinate (exif.fGPSDestLongitudeRef, 4002 exif.fGPSDestLongitude); 4003 4004 if (SyncString (XMP_NS_EXIF, 4005 "GPSDestLongitude", 4006 s, 4007 preferNonXMP + removable)) 4008 { 4009 4010 DecodeGPSCoordinate (s, 4011 exif.fGPSDestLongitudeRef, 4012 exif.fGPSDestLongitude); 4013 4014 } 4015 4016 } 4017 4018 // GPS Destination Bearing Reference: 4019 4020 SyncString (XMP_NS_EXIF, 4021 "GPSDestBearingRef", 4022 exif.fGPSDestBearingRef, 4023 preferNonXMP + removable); 4024 4025 // GPS Destination Bearing: 4026 4027 Sync_urational (XMP_NS_EXIF, 4028 "GPSDestBearing", 4029 exif.fGPSDestBearing, 4030 preferNonXMP + removable); 4031 4032 // GPS Destination Distance Reference: 4033 4034 SyncString (XMP_NS_EXIF, 4035 "GPSDestDistanceRef", 4036 exif.fGPSDestDistanceRef, 4037 preferNonXMP + removable); 4038 4039 // GPS Destination Distance: 4040 4041 Sync_urational (XMP_NS_EXIF, 4042 "GPSDestDistance", 4043 exif.fGPSDestDistance, 4044 preferNonXMP + removable); 4045 4046 // GPS Processing Method: 4047 4048 SyncString (XMP_NS_EXIF, 4049 "GPSProcessingMethod", 4050 exif.fGPSProcessingMethod, 4051 preferNonXMP + removable); 4052 4053 // GPS Area Information: 4054 4055 SyncString (XMP_NS_EXIF, 4056 "GPSAreaInformation", 4057 exif.fGPSAreaInformation, 4058 preferNonXMP + removable); 4059 4060 // GPS Differential: 4061 4062 Sync_uint32 (XMP_NS_EXIF, 4063 "GPSDifferential", 4064 exif.fGPSDifferential, 4065 exif.fGPSDifferential == 0xFFFFFFFF, 4066 preferNonXMP + removable); 4067 4068 // GPS Horizontal Positioning Error: 4069 4070 Sync_urational (XMP_NS_EXIF, 4071 "GPSHPositioningError", 4072 exif.fGPSHPositioningError, 4073 preferNonXMP + removable); 4074 4075 // Sync date/times. 4076 4077 UpdateExifDates (exif, removeFromXMP); 4078 4079 // EXIF 2.3.1 tags. 4080 4081 Sync_srational (XMP_NS_EXIFEX, 4082 "Temperature", 4083 exif.fTemperature, 4084 readOnly + removable); 4085 4086 Sync_urational (XMP_NS_EXIFEX, 4087 "Humidity", 4088 exif.fHumidity, 4089 readOnly + removable); 4090 4091 Sync_urational (XMP_NS_EXIFEX, 4092 "Pressure", 4093 exif.fPressure, 4094 readOnly + removable); 4095 4096 Sync_srational (XMP_NS_EXIFEX, 4097 "WaterDepth", 4098 exif.fWaterDepth, 4099 readOnly + removable); 4100 4101 Sync_urational (XMP_NS_EXIFEX, 4102 "Acceleration", 4103 exif.fAcceleration, 4104 readOnly + removable); 4105 4106 Sync_srational (XMP_NS_EXIFEX, 4107 "CameraElevationAngle", 4108 exif.fCameraElevationAngle, 4109 readOnly + removable); 4110 4111 // We are syncing EXIF and XMP, but we are not updating the 4112 // NativeDigest tags. It is better to just delete them than leave 4113 // the stale values around. 4114 4115 Remove (XMP_NS_EXIF, "NativeDigest"); 4116 Remove (XMP_NS_TIFF, "NativeDigest"); 4117 4118 // Fake EXIF fields. 4119 4120 SyncAltLangDefault (XMP_NS_DC, 4121 "title", 4122 exif.fTitle, 4123 preferXMP); 4124 4125 } 4126 4127 /*****************************************************************************/ 4128 4129 void dng_xmp::SyncApproximateFocusDistance (dng_exif &exif, 4130 const uint32 readOnly) 4131 { 4132 4133 Sync_urational (XMP_NS_AUX, 4134 "ApproximateFocusDistance", 4135 exif.fApproxFocusDistance, 4136 readOnly); 4137 4138 } 4139 4140 /******************************************************************************/ 4141 4142 void dng_xmp::ValidateStringList (const char *ns, 4143 const char *path) 4144 { 4145 4146 fSDK->ValidateStringList (ns, path); 4147 4148 } 4149 4150 /******************************************************************************/ 4151 4152 void dng_xmp::ValidateMetadata () 4153 { 4154 4155 // The following values should be arrays, but are not always. So 4156 // fix them up because Photoshop sometimes has problems parsing invalid 4157 // tags. 4158 4159 ValidateStringList (XMP_NS_DC, "creator"); 4160 4161 ValidateStringList (XMP_NS_PHOTOSHOP, "Keywords"); 4162 ValidateStringList (XMP_NS_PHOTOSHOP, "SupplementalCategories"); 4163 4164 } 4165 4166 /******************************************************************************/ 4167 4168 void dng_xmp::SyncExifDate (const char *ns, 4169 const char *path, 4170 dng_date_time_info &exifDateTime, 4171 bool canRemoveFromXMP, 4172 bool removeFromXMP, 4173 const dng_time_zone &fakeTimeZone) 4174 { 4175 4176 dng_string s; 4177 4178 // Find information on XMP side. 4179 4180 dng_date_time_info xmpDateTime; 4181 4182 if (GetString (ns, path, s)) 4183 { 4184 4185 if (s.IsEmpty ()) 4186 { 4187 4188 // XMP contains an NULL string. Clear EXIF date, 4189 // and remove XMP tag if possible. 4190 4191 exifDateTime.Clear (); 4192 4193 if (canRemoveFromXMP && removeFromXMP) 4194 { 4195 Remove (ns, path); 4196 } 4197 4198 return; 4199 4200 } 4201 4202 xmpDateTime.Decode_ISO_8601 (s.Get ()); 4203 4204 // If the time zone matches the fake time zone, 4205 // ignore it on the XMP side. 4206 4207 if (fakeTimeZone.IsValid () && 4208 xmpDateTime.TimeZone ().IsValid () && 4209 xmpDateTime.TimeZone ().OffsetMinutes () == fakeTimeZone.OffsetMinutes ()) 4210 { 4211 4212 xmpDateTime.ClearZone (); 4213 4214 } 4215 4216 } 4217 4218 // If both are valid, we need to resolve. 4219 4220 if (exifDateTime.IsValid () && xmpDateTime.IsValid ()) 4221 { 4222 4223 // Kludge: The Nikon D4 is writing date only date/times into XMP, so 4224 // prefer the EXIF values if the XMP only contains a date. 4225 4226 if (xmpDateTime.IsDateOnly ()) 4227 { 4228 4229 xmpDateTime = exifDateTime; 4230 4231 } 4232 4233 // Kludge: Nikon sometimes writes XMP values without a time zone 4234 // but the EXIF contains a valid time zone. So in that case, 4235 // prefer the EXIF. This case also deals with sidecar files 4236 // created by pre-Exif 2.3.1 aware cr_sdk versions. 4237 4238 else if (exifDateTime.DateTime () == xmpDateTime.DateTime () && 4239 exifDateTime.TimeZone ().IsValid () && 4240 !xmpDateTime.TimeZone ().IsValid ()) 4241 { 4242 4243 xmpDateTime = exifDateTime; 4244 4245 } 4246 4247 // Else assume that XMP is correct. 4248 4249 else 4250 { 4251 4252 exifDateTime = xmpDateTime; 4253 4254 } 4255 4256 } 4257 4258 // Else just pick the valid one. 4259 4260 else if (xmpDateTime.IsValid ()) 4261 { 4262 4263 exifDateTime = xmpDateTime; 4264 4265 } 4266 4267 else if (exifDateTime.IsValid ()) 4268 { 4269 4270 xmpDateTime = exifDateTime; 4271 4272 } 4273 4274 // Else nothing is valid. 4275 4276 else 4277 { 4278 4279 // Remove XMP side, if any. 4280 4281 Remove (ns, path); 4282 4283 return; 4284 4285 } 4286 4287 // Should we just remove the XMP version? 4288 4289 if (canRemoveFromXMP && removeFromXMP) 4290 { 4291 4292 Remove (ns, path); 4293 4294 } 4295 4296 else 4297 { 4298 4299 s = exifDateTime.Encode_ISO_8601 (); 4300 4301 SetString (ns, path, s); 4302 4303 } 4304 4305 } 4306 4307 /******************************************************************************/ 4308 4309 void dng_xmp::UpdateExifDates (dng_exif &exif, 4310 bool removeFromXMP) 4311 { 4312 4313 // Kludge: Early versions of the XMP library did not support date 4314 // encodings without explict time zones, so software had to fill in 4315 // fake time zones on the XMP side. The usual way was to fill in 4316 // local time zone for the date/time at the import location. 4317 // Attempt to detect these cases and ignore the fake time zones. 4318 4319 dng_time_zone fakeTimeZone; // Initialized to invalid 4320 4321 if (!exif.AtLeastVersion0231 ()) // Real time zones supported in EXIF 2.3.1 4322 { 4323 4324 // Look at DateTimeOriginal since it an EXIF only field (not aliased 4325 // to other fields in XMP) 4326 4327 dng_string s; 4328 4329 if (GetString (XMP_NS_EXIF, "DateTimeOriginal", s) && s.NotEmpty ()) 4330 { 4331 4332 dng_date_time_info xmpDateTimeOriginal; 4333 4334 xmpDateTimeOriginal.Decode_ISO_8601 (s.Get ()); 4335 4336 // If this field has a time zone in XMP, it can only 4337 // be fake. 4338 4339 if (xmpDateTimeOriginal.TimeZone ().IsValid ()) 4340 { 4341 4342 fakeTimeZone = xmpDateTimeOriginal.TimeZone (); 4343 4344 } 4345 4346 } 4347 4348 } 4349 4350 // For the following three date/time fields, we generally prefer XMP to 4351 // the EXIF values. This is to allow the user to correct the date/times 4352 // via changes in a sidecar XMP file, without modifying the original 4353 // raw file. 4354 4355 // Modification Date/Time: 4356 // exif.fDateTime 4357 // kXMP_NS_XMP:"ModifyDate" & kXMP_NS_TIFF:"DateTime" are aliased 4358 4359 SyncExifDate (XMP_NS_TIFF, 4360 "DateTime", 4361 exif.fDateTime, 4362 false, // Cannot remove because aliased 4363 removeFromXMP, 4364 fakeTimeZone); 4365 4366 // Original Date/Time: 4367 // exif.fDateTimeOriginal 4368 // IPTC: DateCreated 4369 // XMP_NS_EXIF:"DateTimeOriginal" & XMP_NS_PHOTOSHOP:"DateCreated" 4370 // Adobe has decided to keep the two XMP fields separate. 4371 4372 { 4373 4374 SyncExifDate (XMP_NS_EXIF, 4375 "DateTimeOriginal", 4376 exif.fDateTimeOriginal, 4377 true, 4378 removeFromXMP, 4379 fakeTimeZone); 4380 4381 // Sync the IPTC value to the EXIF value if only the EXIF 4382 // value exists. 4383 4384 if (exif.fDateTimeOriginal.IsValid ()) 4385 { 4386 4387 // See if the fake time zone was cloned into DateCreated 4388 // field. 4389 4390 bool forceUpdate = false; 4391 4392 if (fakeTimeZone.IsValid ()) 4393 { 4394 4395 dng_string s; 4396 4397 if (GetString (XMP_NS_PHOTOSHOP, "DateCreated", s) && s.NotEmpty ()) 4398 { 4399 4400 dng_date_time_info info; 4401 4402 info.Decode_ISO_8601 (s.Get ()); 4403 4404 if (info.DateTime () == exif.fDateTimeOriginal.DateTime ()) 4405 { 4406 4407 forceUpdate = true; 4408 4409 } 4410 4411 } 4412 4413 } 4414 4415 if (!Exists (XMP_NS_PHOTOSHOP, "DateCreated") || forceUpdate) 4416 { 4417 4418 dng_string s = exif.fDateTimeOriginal.Encode_ISO_8601 (); 4419 4420 SetString (XMP_NS_PHOTOSHOP, "DateCreated", s); 4421 4422 } 4423 4424 } 4425 4426 } 4427 4428 // Date Time Digitized: 4429 // XMP_NS_EXIF:"DateTimeDigitized" & kXMP_NS_XMP:"CreateDate" are aliased 4430 4431 SyncExifDate (XMP_NS_EXIF, 4432 "DateTimeDigitized", 4433 exif.fDateTimeDigitized, 4434 false, // Cannot remove because aliased 4435 removeFromXMP, 4436 fakeTimeZone); 4437 4438 } 4439 4440 /******************************************************************************/ 4441 4442 void dng_xmp::UpdateDateTime (const dng_date_time_info &dt) 4443 { 4444 4445 dng_string s = dt.Encode_ISO_8601 (); 4446 4447 SetString (XMP_NS_TIFF, 4448 "DateTime", 4449 s); 4450 4451 } 4452 4453 /******************************************************************************/ 4454 4455 void dng_xmp::UpdateMetadataDate (const dng_date_time_info &dt) 4456 { 4457 4458 dng_string s = dt.Encode_ISO_8601 (); 4459 4460 SetString (XMP_NS_XAP, 4461 "MetadataDate", 4462 s); 4463 4464 } 4465 4466 /*****************************************************************************/ 4467 4468 bool dng_xmp::HasOrientation () const 4469 { 4470 4471 uint32 x = 0; 4472 4473 if (Get_uint32 (XMP_NS_TIFF, 4474 "Orientation", 4475 x)) 4476 { 4477 4478 return (x >= 1) && (x <= 8); 4479 4480 } 4481 4482 return false; 4483 4484 } 4485 4486 /*****************************************************************************/ 4487 4488 dng_orientation dng_xmp::GetOrientation () const 4489 { 4490 4491 dng_orientation result; 4492 4493 uint32 x = 0; 4494 4495 if (Get_uint32 (XMP_NS_TIFF, 4496 "Orientation", 4497 x)) 4498 { 4499 4500 if ((x >= 1) && (x <= 8)) 4501 { 4502 4503 result.SetTIFF (x); 4504 4505 } 4506 4507 } 4508 4509 return result; 4510 4511 } 4512 4513 /******************************************************************************/ 4514 4515 void dng_xmp::ClearOrientation () 4516 { 4517 4518 fSDK->Remove (XMP_NS_TIFF, "Orientation"); 4519 4520 } 4521 4522 /******************************************************************************/ 4523 4524 void dng_xmp::SetOrientation (const dng_orientation &orientation) 4525 { 4526 4527 Set_uint32 (XMP_NS_TIFF, 4528 "Orientation", 4529 orientation.GetTIFF ()); 4530 4531 } 4532 4533 /*****************************************************************************/ 4534 4535 void dng_xmp::SyncOrientation (dng_negative &negative, 4536 bool xmpIsMaster) 4537 { 4538 4539 SyncOrientation (negative.Metadata (), xmpIsMaster); 4540 4541 } 4542 4543 /*****************************************************************************/ 4544 4545 void dng_xmp::SyncOrientation (dng_metadata &metadata, 4546 bool xmpIsMaster) 4547 { 4548 4549 // See if XMP contains the orientation. 4550 4551 bool xmpHasOrientation = HasOrientation (); 4552 4553 // See if XMP is the master value. 4554 4555 if (xmpHasOrientation && (xmpIsMaster || !metadata.HasBaseOrientation ())) 4556 { 4557 4558 metadata.SetBaseOrientation (GetOrientation ()); 4559 4560 } 4561 4562 else 4563 { 4564 4565 SetOrientation (metadata.BaseOrientation ()); 4566 4567 } 4568 4569 } 4570 4571 /******************************************************************************/ 4572 4573 void dng_xmp::ClearImageInfo () 4574 { 4575 4576 Remove (XMP_NS_TIFF, "ImageWidth" ); 4577 Remove (XMP_NS_TIFF, "ImageLength"); 4578 4579 Remove (XMP_NS_EXIF, "PixelXDimension"); 4580 Remove (XMP_NS_EXIF, "PixelYDimension"); 4581 4582 Remove (XMP_NS_TIFF, "BitsPerSample"); 4583 4584 Remove (XMP_NS_TIFF, "Compression"); 4585 4586 Remove (XMP_NS_TIFF, "PhotometricInterpretation"); 4587 4588 // "Orientation" is handled separately. 4589 4590 Remove (XMP_NS_TIFF, "SamplesPerPixel"); 4591 4592 Remove (XMP_NS_TIFF, "PlanarConfiguration"); 4593 4594 Remove (XMP_NS_TIFF, "XResolution"); 4595 Remove (XMP_NS_TIFF, "YResolution"); 4596 4597 Remove (XMP_NS_TIFF, "ResolutionUnit"); 4598 4599 Remove (XMP_NS_PHOTOSHOP, "ColorMode" ); 4600 Remove (XMP_NS_PHOTOSHOP, "ICCProfile"); 4601 4602 } 4603 4604 /******************************************************************************/ 4605 4606 void dng_xmp::SetImageSize (const dng_point &size) 4607 { 4608 4609 Set_uint32 (XMP_NS_TIFF, "ImageWidth" , size.h); 4610 Set_uint32 (XMP_NS_TIFF, "ImageLength", size.v); 4611 4612 // Mirror these values to the EXIF tags. 4613 4614 Set_uint32 (XMP_NS_EXIF, "PixelXDimension" , size.h); 4615 Set_uint32 (XMP_NS_EXIF, "PixelYDimension" , size.v); 4616 4617 } 4618 4619 /******************************************************************************/ 4620 4621 void dng_xmp::SetSampleInfo (uint32 samplesPerPixel, 4622 uint32 bitsPerSample) 4623 { 4624 4625 Set_uint32 (XMP_NS_TIFF, "SamplesPerPixel", samplesPerPixel); 4626 4627 char s [32]; 4628 4629 sprintf (s, "%u", (unsigned) bitsPerSample); 4630 4631 dng_string ss; 4632 4633 ss.Set (s); 4634 4635 dng_string_list list; 4636 4637 for (uint32 j = 0; j < samplesPerPixel; j++) 4638 { 4639 list.Append (ss); 4640 } 4641 4642 SetStringList (XMP_NS_TIFF, "BitsPerSample", list, false); 4643 4644 } 4645 4646 /******************************************************************************/ 4647 4648 void dng_xmp::SetPhotometricInterpretation (uint32 pi) 4649 { 4650 4651 Set_uint32 (XMP_NS_TIFF, "PhotometricInterpretation", pi); 4652 4653 } 4654 4655 /******************************************************************************/ 4656 4657 void dng_xmp::SetResolution (const dng_resolution &res) 4658 { 4659 4660 Set_urational (XMP_NS_TIFF, "XResolution", res.fXResolution); 4661 Set_urational (XMP_NS_TIFF, "YResolution", res.fYResolution); 4662 4663 Set_uint32 (XMP_NS_TIFF, "ResolutionUnit", res.fResolutionUnit); 4664 4665 } 4666 4667 /*****************************************************************************/ 4668 4669 void dng_xmp::ComposeArrayItemPath (const char *ns, 4670 const char *arrayName, 4671 int32 itemNumber, 4672 dng_string &s) const 4673 { 4674 4675 fSDK->ComposeArrayItemPath (ns, arrayName, itemNumber, s); 4676 4677 } 4678 4679 /*****************************************************************************/ 4680 4681 void dng_xmp::ComposeStructFieldPath (const char *ns, 4682 const char *structName, 4683 const char *fieldNS, 4684 const char *fieldName, 4685 dng_string &s) const 4686 { 4687 4688 fSDK->ComposeStructFieldPath (ns, structName, fieldNS, fieldName, s); 4689 4690 } 4691 4692 /*****************************************************************************/ 4693 4694 int32 dng_xmp::CountArrayItems (const char *ns, 4695 const char *path) const 4696 { 4697 4698 return fSDK->CountArrayItems (ns, path); 4699 4700 } 4701 4702 /*****************************************************************************/ 4703 4704 void dng_xmp::AppendArrayItem (const char *ns, 4705 const char *arrayName, 4706 const char *itemValue, 4707 bool isBag, 4708 bool propIsStruct) 4709 { 4710 4711 fSDK->AppendArrayItem (ns, 4712 arrayName, 4713 itemValue, 4714 isBag, 4715 propIsStruct); 4716 } 4717 4718 /*****************************************************************************/ 4719 4720 #if qDNGXMPDocOps 4721 4722 /*****************************************************************************/ 4723 4724 void dng_xmp::DocOpsOpenXMP (const char *srcMIME) 4725 { 4726 4727 fSDK->DocOpsOpenXMP (srcMIME); 4728 4729 } 4730 4731 /*****************************************************************************/ 4732 4733 void dng_xmp::DocOpsPrepareForSave (const char *srcMIME, 4734 const char *dstMIME, 4735 bool newPath) 4736 { 4737 4738 fSDK->DocOpsPrepareForSave (srcMIME, 4739 dstMIME, 4740 newPath); 4741 4742 } 4743 4744 /*****************************************************************************/ 4745 4746 void dng_xmp::DocOpsUpdateMetadata (const char *srcMIME) 4747 { 4748 4749 fSDK->DocOpsUpdateMetadata (srcMIME); 4750 4751 } 4752 4753 /*****************************************************************************/ 4754 4755 #endif 4756 4757 /*****************************************************************************/