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