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                                 &degrees,
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 /*****************************************************************************/