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