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_sdk.h"
10
11 #include "dng_auto_ptr.h"
12 #include "dng_assertions.h"
13 #include "dng_exceptions.h"
14 #include "dng_flags.h"
15 #include "dng_host.h"
16 #include "dng_local_string.h"
17 #include "dng_memory.h"
18 #include "dng_string.h"
19 #include "dng_string_list.h"
20 #include "dng_utils.h"
21
22 /*****************************************************************************/
23
24 #if qMacOS
25 #ifndef MAC_ENV
26 #define MAC_ENV 1
27 #endif
28 #endif
29
30 #if qWinOS
31 #ifndef WIN_ENV
32 #define WIN_ENV 1
33 #endif
34 #endif
35
36 #include <new>
37 #include <string>
38
39 #define TXMP_STRING_TYPE std::string
40
41 #define XMP_INCLUDE_XMPFILES qDNGXMPFiles
42
43 #define XMP_StaticBuild 1
44
45 #if qiPhone
46 #undef UNIX_ENV
47 #endif
48
49 #include "XMP.incl_cpp"
50
51 /*****************************************************************************/
52
53 const char *XMP_NS_TIFF = "http://ns.adobe.com/tiff/1.0/";
54 const char *XMP_NS_EXIF = "http://ns.adobe.com/exif/1.0/";
55 const char *XMP_NS_EXIFEX = "http://cipa.jp/exif/1.0/";
56 const char *XMP_NS_PHOTOSHOP = "http://ns.adobe.com/photoshop/1.0/";
57 const char *XMP_NS_XAP = "http://ns.adobe.com/xap/1.0/";
58 const char *XMP_NS_XAP_RIGHTS = "http://ns.adobe.com/xap/1.0/rights/";
59 const char *XMP_NS_DC = "http://purl.org/dc/elements/1.1/";
60 const char *XMP_NS_XMP_NOTE = "http://ns.adobe.com/xmp/note/";
61 const char *XMP_NS_MM = "http://ns.adobe.com/xap/1.0/mm/";
62
63 const char *XMP_NS_CRS = "http://ns.adobe.com/camera-raw-settings/1.0/";
64 const char *XMP_NS_CRSS = "http://ns.adobe.com/camera-raw-saved-settings/1.0/";
65 const char *XMP_NS_CRD = "http://ns.adobe.com/camera-raw-defaults/1.0/";
66
67 const char *XMP_NS_LCP = "http://ns.adobe.com/photoshop/1.0/camera-profile";
68
69 const char *XMP_NS_AUX = "http://ns.adobe.com/exif/1.0/aux/";
70
71 const char *XMP_NS_IPTC = "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/";
72 const char *XMP_NS_IPTC_EXT = "http://iptc.org/std/Iptc4xmpExt/2008-02-29/";
73
74 const char *XMP_NS_CRX = "http://ns.adobe.com/lightroom-settings-experimental/1.0/";
75
76 const char *XMP_NS_DNG = "http://ns.adobe.com/dng/1.0/";
77
78 const char *XMP_NS_PANO = "http://ns.adobe.com/photoshop/1.0/panorama-profile";
79
80 /******************************************************************************/
81
82 #define CATCH_XMP_ALT(routine, fatal, silent)\
83 \
84 catch (std::bad_alloc &)\
85 {\
86 DNG_REPORT ("Info: XMP " routine " threw memory exception");\
87 ThrowMemoryFull ();\
88 }\
89 \
90 catch (XMP_Error &error)\
91 {\
92 const char *errMessage = error.GetErrMsg ();\
93 if (errMessage && strlen (errMessage) <= 128)\
94 {\
95 if (!silent)\
96 {\
97 char errBuffer [256];\
98 sprintf (errBuffer, "Info: XMP " routine " threw '%s' exception", errMessage);\
99 DNG_REPORT (errBuffer);\
100 }\
101 }\
102 else\
103 {\
104 DNG_REPORT ("Info: XMP " routine " threw unnamed exception");\
105 }\
106 if (fatal) ThrowProgramError ();\
107 }\
108 \
109 catch (...)\
110 {\
111 DNG_REPORT ("Info: XMP " routine " threw unknown exception");\
112 if (fatal) ThrowProgramError ();\
113 }
114
115 #define CATCH_XMP(routine, fatal) CATCH_XMP_ALT(routine, fatal, false)
116
117 /*****************************************************************************/
118
119 class dng_xmp_private
120 {
121
122 public:
123
124 SXMPMeta *fMeta;
125
dng_xmp_private()126 dng_xmp_private ()
127 : fMeta (NULL)
128 {
129 }
130
131 dng_xmp_private (const dng_xmp_private &xmp);
132
~dng_xmp_private()133 ~dng_xmp_private ()
134 {
135 if (fMeta)
136 {
137 delete fMeta;
138 }
139 }
140
141 private:
142
143 // Hidden assignment operator.
144
145 dng_xmp_private & operator= (const dng_xmp_private &xmp);
146
147 };
148
149 /*****************************************************************************/
150
dng_xmp_private(const dng_xmp_private & xmp)151 dng_xmp_private::dng_xmp_private (const dng_xmp_private &xmp)
152
153 : fMeta (NULL)
154
155 {
156
157 if (xmp.fMeta)
158 {
159
160 fMeta = new SXMPMeta (xmp.fMeta->Clone (0));
161
162 if (!fMeta)
163 {
164 ThrowMemoryFull ();
165 }
166
167 }
168
169 }
170
171 /*****************************************************************************/
172
dng_xmp_sdk()173 dng_xmp_sdk::dng_xmp_sdk ()
174
175 : fPrivate (NULL)
176
177 {
178
179 fPrivate = new dng_xmp_private;
180
181 if (!fPrivate)
182 {
183 ThrowMemoryFull ();
184 }
185
186 }
187
188 /*****************************************************************************/
189
dng_xmp_sdk(const dng_xmp_sdk & sdk)190 dng_xmp_sdk::dng_xmp_sdk (const dng_xmp_sdk &sdk)
191
192 : fPrivate (NULL)
193
194 {
195
196 fPrivate = new dng_xmp_private (*sdk.fPrivate);
197
198 if (!fPrivate)
199 {
200 ThrowMemoryFull ();
201 }
202
203 }
204
205 /*****************************************************************************/
206
~dng_xmp_sdk()207 dng_xmp_sdk::~dng_xmp_sdk ()
208 {
209
210 if (fPrivate)
211 {
212 delete fPrivate;
213 }
214
215 }
216
217 /*****************************************************************************/
218
219 static bool gInitializedXMP = false;
220
221 /*****************************************************************************/
222
InitializeSDK(dng_xmp_namespace * extraNamespaces,const char * software)223 void dng_xmp_sdk::InitializeSDK (dng_xmp_namespace * extraNamespaces,
224 const char *software)
225 {
226
227 if (!gInitializedXMP)
228 {
229
230 try
231 {
232
233 if (!SXMPMeta::Initialize ())
234 {
235 ThrowProgramError ();
236 }
237
238 // Register Lightroom beta settings namespace.
239 // We no longer read this, but we do need to register
240 // it so we can clean up this namespace when saving
241 // new settings.
242
243 {
244
245 TXMP_STRING_TYPE ss;
246
247 SXMPMeta::RegisterNamespace (XMP_NS_CRX,
248 "crx",
249 &ss);
250
251 }
252
253 // Register CRSS snapshots namespace
254
255 {
256
257 TXMP_STRING_TYPE ss;
258
259 SXMPMeta::RegisterNamespace (XMP_NS_CRSS,
260 "crss",
261 &ss);
262
263 }
264
265 // Register CRD defaults namespace
266
267 {
268
269 TXMP_STRING_TYPE ss;
270
271 SXMPMeta::RegisterNamespace (XMP_NS_CRD,
272 "crd",
273 &ss);
274
275 }
276
277 // Register LCP (lens correction profiles) namespace
278
279 {
280
281 TXMP_STRING_TYPE ss;
282
283 SXMPMeta::RegisterNamespace (XMP_NS_LCP,
284 "stCamera",
285 &ss);
286
287 }
288
289 // Register DNG format metadata namespace
290
291 {
292
293 TXMP_STRING_TYPE ss;
294
295 SXMPMeta::RegisterNamespace (XMP_NS_DNG,
296 "dng",
297 &ss);
298
299 }
300
301 // Register panorama metadata namespace
302
303 {
304
305 TXMP_STRING_TYPE ss;
306
307 SXMPMeta::RegisterNamespace (XMP_NS_PANO,
308 "panorama",
309 &ss);
310
311 }
312
313 // Register extra namespaces.
314
315 if (extraNamespaces != NULL)
316 {
317
318 for (; extraNamespaces->fullName != NULL; ++extraNamespaces)
319 {
320
321 TXMP_STRING_TYPE ss;
322
323 SXMPMeta::RegisterNamespace (extraNamespaces->fullName,
324 extraNamespaces->shortName,
325 &ss);
326
327 }
328
329 }
330
331 #if qDNGXMPFiles
332
333 #if qLinux || qAndroid
334 if (!SXMPFiles::Initialize (kXMPFiles_IgnoreLocalText))
335 #else
336 if (!SXMPFiles::Initialize ())
337 #endif
338 {
339 ThrowProgramError ();
340 }
341
342 #endif
343
344 #if qDNGXMPDocOps
345
346 if (software)
347 {
348
349 SXMPDocOps::SetAppName (software);
350
351 }
352
353 #else
354
355 (void) software;
356
357 #endif
358
359 }
360
361 CATCH_XMP ("Initialization", true)
362
363 gInitializedXMP = true;
364
365 }
366
367 }
368
369 /******************************************************************************/
370
TerminateSDK()371 void dng_xmp_sdk::TerminateSDK ()
372 {
373
374 if (gInitializedXMP)
375 {
376
377 try
378 {
379
380 #if qDNGXMPFiles
381
382 SXMPFiles::Terminate ();
383
384 #endif
385
386 SXMPMeta::Terminate ();
387
388 }
389
390 catch (...)
391 {
392
393 }
394
395 gInitializedXMP = false;
396
397 }
398
399 }
400
401 /******************************************************************************/
402
HasMeta() const403 bool dng_xmp_sdk::HasMeta () const
404 {
405
406 if (fPrivate->fMeta)
407 {
408
409 return true;
410
411 }
412
413 return false;
414
415 }
416
417 /******************************************************************************/
418
ClearMeta()419 void dng_xmp_sdk::ClearMeta ()
420 {
421
422 if (HasMeta ())
423 {
424
425 delete fPrivate->fMeta;
426
427 fPrivate->fMeta = NULL;
428
429 }
430
431 }
432
433 /******************************************************************************/
434
MakeMeta()435 void dng_xmp_sdk::MakeMeta ()
436 {
437
438 ClearMeta ();
439
440 InitializeSDK ();
441
442 try
443 {
444
445 fPrivate->fMeta = new SXMPMeta;
446
447 if (!fPrivate->fMeta)
448 {
449
450 ThrowMemoryFull ();
451
452 }
453
454 }
455
456 CATCH_XMP ("MakeMeta", true)
457
458 }
459
460 /******************************************************************************/
461
NeedMeta()462 void dng_xmp_sdk::NeedMeta ()
463 {
464
465 if (!HasMeta ())
466 {
467
468 MakeMeta ();
469
470 }
471
472 }
473
474 /******************************************************************************/
475
GetPrivateMeta()476 void * dng_xmp_sdk::GetPrivateMeta ()
477 {
478
479 NeedMeta ();
480
481 return (void *) fPrivate->fMeta;
482
483 }
484
485 /******************************************************************************/
486
Parse(dng_host & host,const char * buffer,uint32 count)487 void dng_xmp_sdk::Parse (dng_host &host,
488 const char *buffer,
489 uint32 count)
490 {
491
492 MakeMeta ();
493
494 try
495 {
496
497 try
498 {
499
500 fPrivate->fMeta->ParseFromBuffer (buffer, count);
501
502 }
503
504 CATCH_XMP ("ParseFromBuffer", true)
505
506 }
507
508 catch (dng_exception &except)
509 {
510
511 ClearMeta ();
512
513 if (host.IsTransientError (except.ErrorCode ()))
514 {
515
516 throw;
517
518 }
519
520 ThrowBadFormat ();
521
522 }
523
524 }
525
526 /*****************************************************************************/
527
AppendArrayItem(const char * ns,const char * arrayName,const char * itemValue,bool isBag,bool propIsStruct)528 void dng_xmp_sdk::AppendArrayItem (const char *ns,
529 const char *arrayName,
530 const char *itemValue,
531 bool isBag,
532 bool propIsStruct)
533 {
534
535 NeedMeta();
536
537 try
538 {
539
540 fPrivate->fMeta->AppendArrayItem (ns,
541 arrayName,
542 isBag ? kXMP_PropValueIsArray
543 : kXMP_PropArrayIsOrdered,
544 itemValue,
545 propIsStruct ? kXMP_PropValueIsStruct
546 : 0);
547
548 }
549 CATCH_XMP ("AppendArrayItem", true )
550
551 }
552
553 /*****************************************************************************/
554
CountArrayItems(const char * ns,const char * path) const555 int32 dng_xmp_sdk::CountArrayItems (const char *ns,
556 const char *path) const
557 {
558
559 if (HasMeta ())
560 {
561
562 try
563 {
564
565 return fPrivate->fMeta->CountArrayItems (ns, path);
566
567 }
568
569 CATCH_XMP ("CountArrayItems", false)
570
571 }
572
573 return 0;
574
575 }
576
577 /*****************************************************************************/
578
Exists(const char * ns,const char * path) const579 bool dng_xmp_sdk::Exists (const char *ns,
580 const char *path) const
581 {
582
583 if (HasMeta ())
584 {
585
586 try
587 {
588
589 return fPrivate->fMeta->DoesPropertyExist (ns, path);
590
591 }
592
593 catch (...)
594 {
595
596 // Does not exist...
597
598 }
599
600 }
601
602 return false;
603
604 }
605
606 /*****************************************************************************/
607
HasNameSpace(const char * ns) const608 bool dng_xmp_sdk::HasNameSpace (const char *ns) const
609 {
610
611 bool result = false;
612
613 if (HasMeta ())
614 {
615
616 try
617 {
618
619 SXMPIterator iter (*fPrivate->fMeta, ns);
620
621 TXMP_STRING_TYPE nsTemp;
622 TXMP_STRING_TYPE prop;
623
624 if (iter.Next (&nsTemp,
625 &prop,
626 NULL,
627 NULL))
628 {
629
630 result = true;
631
632 }
633
634 }
635
636 CATCH_XMP ("HasNameSpace", true)
637
638 }
639
640 return result;
641
642 }
643
644 /*****************************************************************************/
645
Remove(const char * ns,const char * path)646 void dng_xmp_sdk::Remove (const char *ns,
647 const char *path)
648 {
649
650 if (HasMeta ())
651 {
652
653 try
654 {
655
656 fPrivate->fMeta->DeleteProperty (ns, path);
657
658 }
659
660 CATCH_XMP ("DeleteProperty", false)
661
662 }
663
664 }
665
666 /*****************************************************************************/
667
RemoveProperties(const char * ns)668 void dng_xmp_sdk::RemoveProperties (const char *ns)
669 {
670
671 if (HasMeta ())
672 {
673
674 try
675 {
676
677 SXMPUtils::RemoveProperties (fPrivate->fMeta,
678 ns,
679 NULL,
680 kXMPUtil_DoAllProperties);
681
682 }
683
684 catch (...)
685 {
686
687 }
688
689 }
690
691 }
692
693 /*****************************************************************************/
694
IsEmptyString(const char * ns,const char * path)695 bool dng_xmp_sdk::IsEmptyString (const char *ns,
696 const char *path)
697 {
698
699 if (HasMeta ())
700 {
701
702 try
703 {
704
705 TXMP_STRING_TYPE ss;
706
707 XMP_OptionBits options = 0;
708
709 if (fPrivate->fMeta->GetProperty (ns,
710 path,
711 &ss,
712 &options))
713 {
714
715 // Item must be simple.
716
717 if (XMP_PropIsSimple (options))
718 {
719
720 // Check for null strings.
721
722 return (ss.c_str () == 0 ||
723 ss.c_str () [0] == 0);
724
725 }
726
727 }
728
729 }
730
731 CATCH_XMP ("IsEmptyString", false)
732
733 }
734
735 return false;
736
737 }
738
739 /*****************************************************************************/
740
IsEmptyArray(const char * ns,const char * path)741 bool dng_xmp_sdk::IsEmptyArray (const char *ns,
742 const char *path)
743 {
744
745 if (HasMeta ())
746 {
747
748 try
749 {
750
751 TXMP_STRING_TYPE ss;
752
753 XMP_OptionBits options = 0;
754
755 if (fPrivate->fMeta->GetProperty (ns,
756 path,
757 &ss,
758 &options))
759 {
760
761 if (XMP_PropIsArray (options))
762 {
763
764 if (fPrivate->fMeta->GetArrayItem (ns,
765 path,
766 1,
767 &ss,
768 &options))
769 {
770
771 // If the first item is a null string...
772
773 if (XMP_PropIsSimple (options))
774 {
775
776 if ((ss.c_str () == 0 ||
777 ss.c_str () [0] == 0))
778 {
779
780 // And there is no second item.
781
782 if (!fPrivate->fMeta->GetArrayItem (ns,
783 path,
784 2,
785 &ss,
786 &options))
787 {
788
789 // Then we have an empty array.
790
791 return true;
792
793 }
794
795 }
796
797 }
798
799 }
800
801 else
802 {
803
804 // Unable to get first item, so array is empty.
805
806 return true;
807
808 }
809
810 }
811
812 }
813
814 }
815
816 CATCH_XMP ("IsEmptyArray", false)
817
818 }
819
820 return false;
821
822 }
823
824 /*****************************************************************************/
825
ComposeArrayItemPath(const char * ns,const char * arrayName,int32 index,dng_string & s) const826 void dng_xmp_sdk::ComposeArrayItemPath (const char *ns,
827 const char *arrayName,
828 int32 index,
829 dng_string &s) const
830 {
831
832 try
833 {
834
835 std::string ss;
836
837 SXMPUtils::ComposeArrayItemPath (ns, arrayName, index, &ss);
838
839 s.Set (ss.c_str ());
840
841 return;
842
843 }
844
845 CATCH_XMP ("ComposeArrayItemPath", true)
846
847 }
848
849 /*****************************************************************************/
850
ComposeStructFieldPath(const char * ns,const char * structName,const char * fieldNS,const char * fieldName,dng_string & s) const851 void dng_xmp_sdk::ComposeStructFieldPath (const char *ns,
852 const char *structName,
853 const char *fieldNS,
854 const char *fieldName,
855 dng_string &s) const
856 {
857
858 try
859 {
860
861 std::string ss;
862
863 SXMPUtils::ComposeStructFieldPath (ns,
864 structName,
865 fieldNS,
866 fieldName,
867 &ss);
868
869 s.Set (ss.c_str ());
870
871 return;
872
873 }
874
875 CATCH_XMP ("ComposeStructFieldPath", true)
876
877 }
878
879 /*****************************************************************************/
880
GetNamespacePrefix(const char * uri,dng_string & s) const881 bool dng_xmp_sdk::GetNamespacePrefix (const char *uri,
882 dng_string &s) const
883 {
884
885 bool result = false;
886
887 if (HasMeta ())
888 {
889
890 try
891 {
892
893 std::string ss;
894
895 fPrivate->fMeta->GetNamespacePrefix (uri, &ss);
896
897 s.Set (ss.c_str ());
898
899 result = true;
900
901 }
902
903 CATCH_XMP ("GetNamespacePrefix", false)
904
905 }
906
907 return result;
908
909 }
910
911 /*****************************************************************************/
912
GetString(const char * ns,const char * path,dng_string & s) const913 bool dng_xmp_sdk::GetString (const char *ns,
914 const char *path,
915 dng_string &s) const
916 {
917
918 bool result = false;
919
920 if (HasMeta ())
921 {
922
923 try
924 {
925
926 TXMP_STRING_TYPE ss;
927
928 if (fPrivate->fMeta->GetProperty (ns, path, &ss, NULL))
929 {
930
931 s.Set (ss.c_str ());
932
933 result = true;
934
935 }
936
937 }
938
939 CATCH_XMP ("GetProperty", false)
940
941 }
942
943 return result;
944
945 }
946
947 /*****************************************************************************/
948
ValidateStringList(const char * ns,const char * path)949 void dng_xmp_sdk::ValidateStringList (const char *ns,
950 const char *path)
951 {
952
953 if (Exists (ns, path))
954 {
955
956 bool bogus = true;
957
958 try
959 {
960
961 XMP_Index index = 1;
962
963 TXMP_STRING_TYPE ss;
964
965 while (fPrivate->fMeta->GetArrayItem (ns,
966 path,
967 index++,
968 &ss,
969 NULL))
970 {
971
972 }
973
974 bogus = false;
975
976 }
977
978 catch (...)
979 {
980
981 // Array is probably bogus. Don't need to report.
982
983 }
984
985 if (bogus)
986 {
987
988 Remove (ns, path);
989
990 }
991
992 }
993
994 }
995
996 /*****************************************************************************/
997
GetStringList(const char * ns,const char * path,dng_string_list & list) const998 bool dng_xmp_sdk::GetStringList (const char *ns,
999 const char *path,
1000 dng_string_list &list) const
1001 {
1002
1003 bool result = false;
1004
1005 if (HasMeta ())
1006 {
1007
1008 try
1009 {
1010
1011 XMP_Index index = 1;
1012
1013 TXMP_STRING_TYPE ss;
1014
1015 while (fPrivate->fMeta->GetArrayItem (ns,
1016 path,
1017 index++,
1018 &ss,
1019 NULL))
1020 {
1021
1022 dng_string s;
1023
1024 s.Set (ss.c_str ());
1025
1026 list.Append (s);
1027
1028 result = true;
1029
1030 }
1031
1032 }
1033
1034 CATCH_XMP ("GetStringList", false)
1035
1036 }
1037
1038 return result;
1039
1040 }
1041
1042 /*****************************************************************************/
1043
GetAltLangDefault(const char * ns,const char * path,dng_string & s,bool silent) const1044 bool dng_xmp_sdk::GetAltLangDefault (const char *ns,
1045 const char *path,
1046 dng_string &s,
1047 bool silent) const
1048 {
1049
1050 bool result = false;
1051
1052 if (HasMeta ())
1053 {
1054
1055 try
1056 {
1057
1058 TXMP_STRING_TYPE ss;
1059
1060 if (fPrivate->fMeta->GetLocalizedText (ns,
1061 path,
1062 "x-default",
1063 "x-default",
1064 NULL,
1065 &ss,
1066 NULL))
1067 {
1068
1069 s.Set (ss.c_str ());
1070
1071 result = true;
1072
1073 }
1074
1075 // Special Case: treat the following two representation equivalently.
1076 // The first is an empty alt lang array; the second is an array with
1077 // an empty item. It seems that xmp lib could be generating both under
1078 // some circumstances!
1079 //
1080 // <dc:description>
1081 // <rdf:Alt/>
1082 // </dc:description>
1083 //
1084 // and
1085 //
1086 // <dc:description>
1087 // <rdf:Alt>
1088 // <rdf:li xml:lang="x-default"/>
1089 // </rdf:Alt>
1090 // </dc:description>
1091
1092 else if (fPrivate->fMeta->GetProperty (ns,
1093 path,
1094 &ss,
1095 NULL))
1096 {
1097
1098 if (ss.empty ())
1099 {
1100
1101 s.Clear ();
1102
1103 result = true;
1104
1105 }
1106
1107 }
1108
1109 }
1110
1111 CATCH_XMP_ALT ("GetLocalizedText", false, silent)
1112
1113 }
1114
1115 return result;
1116
1117 }
1118
1119 /*****************************************************************************/
1120
GetLocalString(const char * ns,const char * path,dng_local_string & s) const1121 bool dng_xmp_sdk::GetLocalString (const char *ns,
1122 const char *path,
1123 dng_local_string &s) const
1124 {
1125
1126 dng_string defaultText;
1127
1128 if (GetAltLangDefault (ns, path, defaultText, true))
1129 {
1130
1131 s.SetDefaultText (defaultText);
1132
1133 try
1134 {
1135
1136 int32 count = CountArrayItems (ns, path);
1137
1138 if (count > 1)
1139 {
1140
1141 for (int32 index = 1; index <= count; index++)
1142 {
1143
1144 dng_string arrayItemPath;
1145
1146 ComposeArrayItemPath (ns,
1147 path,
1148 index + 1,
1149 arrayItemPath);
1150
1151 TXMP_STRING_TYPE langS;
1152
1153 if (fPrivate->fMeta->GetQualifier (ns,
1154 arrayItemPath.Get (),
1155 kXMP_NS_XML,
1156 "lang",
1157 &langS,
1158 NULL))
1159 {
1160
1161 dng_string language;
1162
1163 language.Set (langS.c_str ());
1164
1165 if (language.IsEmpty () ||
1166 language.Matches ("x-default"))
1167 {
1168 continue;
1169 }
1170
1171 TXMP_STRING_TYPE tranS;
1172
1173 if (fPrivate->fMeta->GetProperty (ns,
1174 arrayItemPath.Get (),
1175 &tranS,
1176 NULL))
1177 {
1178
1179 dng_string translation;
1180
1181 translation.Set (tranS.c_str ());
1182
1183 s.AddTranslation (language,
1184 translation);
1185
1186 }
1187
1188 }
1189
1190 }
1191
1192 }
1193
1194 }
1195
1196 CATCH_XMP ("GetLocalString", false)
1197
1198 return true;
1199
1200 }
1201
1202 return false;
1203
1204 }
1205
1206 /*****************************************************************************/
1207
GetStructField(const char * ns,const char * path,const char * fieldNS,const char * fieldName,dng_string & s) const1208 bool dng_xmp_sdk::GetStructField (const char *ns,
1209 const char *path,
1210 const char *fieldNS,
1211 const char *fieldName,
1212 dng_string &s) const
1213 {
1214
1215 bool result = false;
1216
1217 if (HasMeta ())
1218 {
1219
1220 try
1221 {
1222
1223 TXMP_STRING_TYPE ss;
1224
1225 if (fPrivate->fMeta->GetStructField (ns,
1226 path,
1227 fieldNS,
1228 fieldName,
1229 &ss,
1230 NULL))
1231 {
1232
1233 s.Set (ss.c_str ());
1234
1235 result = true;
1236
1237 }
1238
1239 }
1240
1241 CATCH_XMP ("GetStructField", false)
1242
1243 }
1244
1245 return result;
1246
1247 }
1248
1249 /*****************************************************************************/
1250
Set(const char * ns,const char * path,const char * text)1251 void dng_xmp_sdk::Set (const char *ns,
1252 const char *path,
1253 const char *text)
1254 {
1255
1256 NeedMeta ();
1257
1258 try
1259 {
1260
1261 fPrivate->fMeta->SetProperty (ns, path, text);
1262
1263 return;
1264
1265 }
1266
1267 catch (...)
1268 {
1269
1270 // Failed for some reason.
1271
1272 }
1273
1274 // Remove existing value and try again.
1275
1276 Remove (ns, path);
1277
1278 try
1279 {
1280
1281 fPrivate->fMeta->SetProperty (ns, path, text);
1282
1283 }
1284
1285 CATCH_XMP ("SetProperty", true)
1286
1287 }
1288
1289 /*****************************************************************************/
1290
SetString(const char * ns,const char * path,const dng_string & s)1291 void dng_xmp_sdk::SetString (const char *ns,
1292 const char *path,
1293 const dng_string &s)
1294 {
1295
1296 dng_string ss (s);
1297
1298 ss.SetLineEndings ('\n');
1299
1300 ss.StripLowASCII ();
1301
1302 Set (ns, path, ss.Get ());
1303
1304 }
1305
1306 /*****************************************************************************/
1307
SetStringList(const char * ns,const char * path,const dng_string_list & list,bool isBag)1308 void dng_xmp_sdk::SetStringList (const char *ns,
1309 const char *path,
1310 const dng_string_list &list,
1311 bool isBag)
1312 {
1313
1314 // Remove any existing structure.
1315
1316 Remove (ns, path);
1317
1318 // If list is not empty, add the items.
1319
1320 if (list.Count ())
1321 {
1322
1323 NeedMeta ();
1324
1325 for (uint32 index = 0; index < list.Count (); index++)
1326 {
1327
1328 dng_string s (list [index]);
1329
1330 s.SetLineEndings ('\n');
1331
1332 s.StripLowASCII ();
1333
1334 try
1335 {
1336
1337 fPrivate->fMeta->AppendArrayItem (ns,
1338 path,
1339 isBag ? kXMP_PropValueIsArray
1340 : kXMP_PropArrayIsOrdered,
1341 s.Get ());
1342
1343 }
1344
1345 CATCH_XMP ("AppendArrayItem", true)
1346
1347 }
1348
1349 }
1350
1351 }
1352
1353 /*****************************************************************************/
1354
SetAltLangDefault(const char * ns,const char * path,const dng_string & s)1355 void dng_xmp_sdk::SetAltLangDefault (const char *ns,
1356 const char *path,
1357 const dng_string &s)
1358 {
1359
1360 NeedMeta ();
1361
1362 Remove (ns, path);
1363
1364 dng_string ss (s);
1365
1366 ss.SetLineEndings ('\n');
1367
1368 ss.StripLowASCII ();
1369
1370 try
1371 {
1372
1373 fPrivate->fMeta->SetLocalizedText (ns,
1374 path,
1375 "x-default",
1376 "x-default",
1377 ss.Get ());
1378
1379 }
1380
1381 CATCH_XMP ("SetLocalizedText", true)
1382
1383 }
1384
1385 /*****************************************************************************/
1386
SetLocalString(const char * ns,const char * path,const dng_local_string & s)1387 void dng_xmp_sdk::SetLocalString (const char *ns,
1388 const char *path,
1389 const dng_local_string &s)
1390 {
1391
1392 SetAltLangDefault (ns, path, s.DefaultText ());
1393
1394 try
1395 {
1396
1397 for (uint32 index = 0; index < s.TranslationCount (); index++)
1398 {
1399
1400 dng_string arrayItemPath;
1401
1402 ComposeArrayItemPath (ns,
1403 path,
1404 index + 2,
1405 arrayItemPath);
1406
1407 fPrivate->fMeta->SetProperty (ns,
1408 arrayItemPath.Get (),
1409 s.Translation (index).Get ());
1410
1411 fPrivate->fMeta->SetQualifier (ns,
1412 arrayItemPath.Get (),
1413 kXMP_NS_XML,
1414 "lang",
1415 s.Language (index).Get (),
1416 0);
1417
1418 }
1419
1420 }
1421
1422 CATCH_XMP ("SetLocalizedText", true)
1423
1424 }
1425
1426 /*****************************************************************************/
1427
SetStructField(const char * ns,const char * path,const char * fieldNS,const char * fieldName,const char * text)1428 void dng_xmp_sdk::SetStructField (const char *ns,
1429 const char *path,
1430 const char *fieldNS,
1431 const char *fieldName,
1432 const char *text)
1433 {
1434
1435 NeedMeta ();
1436
1437 try
1438 {
1439
1440 fPrivate->fMeta->SetStructField (ns,
1441 path,
1442 fieldNS,
1443 fieldName,
1444 text);
1445
1446 }
1447
1448 CATCH_XMP ("SetStructField", true)
1449
1450 }
1451
1452 /*****************************************************************************/
1453
DeleteStructField(const char * ns,const char * structName,const char * fieldNS,const char * fieldName)1454 void dng_xmp_sdk::DeleteStructField (const char *ns,
1455 const char *structName,
1456 const char *fieldNS,
1457 const char *fieldName)
1458 {
1459
1460 if (HasMeta ())
1461 {
1462
1463 try
1464 {
1465
1466 fPrivate->fMeta->DeleteStructField (ns, structName, fieldNS, fieldName);
1467
1468 }
1469
1470 catch (...)
1471 {
1472
1473 }
1474
1475 }
1476
1477 }
1478
1479 /*****************************************************************************/
1480
Serialize(dng_memory_allocator & allocator,bool asPacket,uint32 targetBytes,uint32 padBytes,bool forJPEG,bool compact) const1481 dng_memory_block * dng_xmp_sdk::Serialize (dng_memory_allocator &allocator,
1482 bool asPacket,
1483 uint32 targetBytes,
1484 uint32 padBytes,
1485 bool forJPEG,
1486 bool compact) const
1487 {
1488
1489 // The largest XMP packet you can embed in JPEG using normal methods:
1490
1491 const uint32 kJPEG_XMP_Limit = 65504;
1492
1493 if (HasMeta ())
1494 {
1495
1496 TXMP_STRING_TYPE s;
1497
1498 bool havePacket = false;
1499
1500 // Note that the XMP lib is changing its default to compact format
1501 // in the future, so the following line will need to change.
1502
1503 uint32 formatOption = compact ? kXMP_UseCompactFormat : 0;
1504
1505 if (asPacket && targetBytes)
1506 {
1507
1508 try
1509 {
1510
1511 fPrivate->fMeta->SerializeToBuffer (&s,
1512 formatOption | kXMP_ExactPacketLength,
1513 targetBytes,
1514 "",
1515 " ");
1516
1517 havePacket = true;
1518
1519 }
1520
1521 catch (...)
1522 {
1523
1524 // Most likely the packet cannot fit in the target
1525 // byte count. So try again without the limit.
1526
1527 }
1528
1529 }
1530
1531 if (!havePacket)
1532 {
1533
1534 try
1535 {
1536
1537 fPrivate->fMeta->SerializeToBuffer (&s,
1538 formatOption |
1539 (asPacket ? 0
1540 : kXMP_OmitPacketWrapper),
1541 (asPacket ? padBytes
1542 : 0),
1543 "",
1544 " ");
1545
1546 }
1547
1548 CATCH_XMP ("SerializeToBuffer", true)
1549
1550 }
1551
1552 uint32 packetLen = (uint32) s.size ();
1553
1554 if (forJPEG && asPacket && padBytes > 0 && targetBytes <= kJPEG_XMP_Limit &&
1555 packetLen > kJPEG_XMP_Limit)
1556 {
1557
1558 uint32 overLimitCount = packetLen - kJPEG_XMP_Limit;
1559
1560 if (overLimitCount > padBytes)
1561 {
1562 padBytes = 0;
1563 }
1564 else
1565 {
1566 padBytes -= overLimitCount;
1567 }
1568
1569 try
1570 {
1571
1572 fPrivate->fMeta->SerializeToBuffer (&s,
1573 formatOption,
1574 padBytes,
1575 "",
1576 " ");
1577
1578 }
1579
1580 CATCH_XMP ("SerializeToBuffer", true)
1581
1582 packetLen = (uint32) s.size ();
1583
1584 }
1585
1586 if (packetLen)
1587 {
1588
1589 AutoPtr<dng_memory_block> buffer (allocator.Allocate (packetLen));
1590
1591 memcpy (buffer->Buffer (), s.c_str (), packetLen);
1592
1593 return buffer.Release ();
1594
1595 }
1596
1597 }
1598
1599 return NULL;
1600
1601 }
1602
1603 /*****************************************************************************/
1604
PackageForJPEG(dng_memory_allocator & allocator,AutoPtr<dng_memory_block> & stdBlock,AutoPtr<dng_memory_block> & extBlock,dng_string & extDigest) const1605 void dng_xmp_sdk::PackageForJPEG (dng_memory_allocator &allocator,
1606 AutoPtr<dng_memory_block> &stdBlock,
1607 AutoPtr<dng_memory_block> &extBlock,
1608 dng_string &extDigest) const
1609 {
1610
1611 if (HasMeta ())
1612 {
1613
1614 TXMP_STRING_TYPE stdStr;
1615 TXMP_STRING_TYPE extStr;
1616 TXMP_STRING_TYPE digestStr;
1617
1618 try
1619 {
1620
1621 SXMPUtils::PackageForJPEG (*fPrivate->fMeta,
1622 &stdStr,
1623 &extStr,
1624 &digestStr);
1625
1626 }
1627
1628 CATCH_XMP ("PackageForJPEG", true)
1629
1630 uint32 stdLen = (uint32) stdStr.size ();
1631 uint32 extLen = (uint32) extStr.size ();
1632
1633 if (stdLen)
1634 {
1635
1636 stdBlock.Reset (allocator.Allocate (stdLen));
1637
1638 memcpy (stdBlock->Buffer (), stdStr.c_str (), stdLen);
1639
1640 }
1641
1642 if (extLen)
1643 {
1644
1645 extBlock.Reset (allocator.Allocate (extLen));
1646
1647 memcpy (extBlock->Buffer (), extStr.c_str (), extLen);
1648
1649 if (digestStr.size () != 32)
1650 {
1651 ThrowProgramError ();
1652 }
1653
1654 extDigest.Set (digestStr.c_str ());
1655
1656 }
1657
1658 }
1659
1660 }
1661
1662 /*****************************************************************************/
1663
MergeFromJPEG(const dng_xmp_sdk * xmp)1664 void dng_xmp_sdk::MergeFromJPEG (const dng_xmp_sdk *xmp)
1665 {
1666
1667 if (xmp && xmp->HasMeta ())
1668 {
1669
1670 NeedMeta ();
1671
1672 try
1673 {
1674
1675 SXMPUtils::MergeFromJPEG (fPrivate->fMeta,
1676 *xmp->fPrivate->fMeta);
1677
1678 }
1679
1680 CATCH_XMP ("MergeFromJPEG", true)
1681
1682 }
1683
1684 }
1685
1686 /*****************************************************************************/
1687
ReplaceXMP(dng_xmp_sdk * xmp)1688 void dng_xmp_sdk::ReplaceXMP (dng_xmp_sdk *xmp)
1689 {
1690
1691 ClearMeta ();
1692
1693 if (xmp && xmp->HasMeta ())
1694 {
1695
1696 fPrivate->fMeta = xmp->fPrivate->fMeta;
1697
1698 xmp->fPrivate->fMeta = NULL;
1699
1700 }
1701
1702 }
1703
1704 /*****************************************************************************/
1705
IteratePaths(IteratePathsCallback * callback,void * callbackData,const char * startingNS,const char * startingPath)1706 bool dng_xmp_sdk::IteratePaths (IteratePathsCallback *callback,
1707 void *callbackData,
1708 const char* startingNS,
1709 const char* startingPath)
1710 {
1711
1712 if (HasMeta ())
1713 {
1714
1715 try
1716 {
1717
1718 SXMPIterator iter (*fPrivate->fMeta, startingNS, startingPath);
1719
1720 TXMP_STRING_TYPE ns;
1721 TXMP_STRING_TYPE prop;
1722
1723 while (iter.Next (&ns,
1724 &prop,
1725 NULL,
1726 NULL))
1727 {
1728
1729 if (!callback (ns .c_str (),
1730 prop.c_str (),
1731 callbackData))
1732 {
1733
1734 return false;
1735
1736 }
1737
1738 }
1739
1740 }
1741
1742 CATCH_XMP ("IteratePaths", true)
1743
1744 }
1745
1746 return true;
1747
1748 }
1749
1750 /*****************************************************************************/
1751
1752 #if qDNGXMPDocOps
1753
1754 /*****************************************************************************/
1755
DocOpsOpenXMP(const char * srcMIME)1756 void dng_xmp_sdk::DocOpsOpenXMP (const char *srcMIME)
1757 {
1758
1759 if (srcMIME [0])
1760 {
1761
1762 NeedMeta ();
1763
1764 try
1765 {
1766
1767 SXMPDocOps docOps;
1768
1769 docOps.OpenXMP (fPrivate->fMeta,
1770 srcMIME);
1771
1772 }
1773
1774 CATCH_XMP ("DocOpsOpenXMP", false)
1775
1776 Set (XMP_NS_DC,
1777 "format",
1778 srcMIME);
1779
1780 }
1781
1782 }
1783
1784 /*****************************************************************************/
1785
DocOpsPrepareForSave(const char * srcMIME,const char * dstMIME,bool newPath)1786 void dng_xmp_sdk::DocOpsPrepareForSave (const char *srcMIME,
1787 const char *dstMIME,
1788 bool newPath)
1789 {
1790
1791 NeedMeta ();
1792
1793 try
1794 {
1795
1796 SXMPDocOps docOps;
1797
1798 docOps.OpenXMP (fPrivate->fMeta,
1799 srcMIME,
1800 "old path");
1801
1802 docOps.NoteChange (kXMP_Part_All);
1803
1804 docOps.PrepareForSave (dstMIME,
1805 newPath ? "new path" : "old path");
1806
1807 }
1808
1809 CATCH_XMP ("DocOpsPrepareForSave", false)
1810
1811 Set (XMP_NS_DC,
1812 "format",
1813 dstMIME);
1814
1815 }
1816
1817 /*****************************************************************************/
1818
DocOpsUpdateMetadata(const char * srcMIME)1819 void dng_xmp_sdk::DocOpsUpdateMetadata (const char *srcMIME)
1820 {
1821
1822 NeedMeta ();
1823
1824 try
1825 {
1826
1827 SXMPDocOps docOps;
1828
1829 docOps.OpenXMP (fPrivate->fMeta,
1830 srcMIME);
1831
1832 docOps.NoteChange (kXMP_Part_Metadata);
1833
1834 docOps.PrepareForSave (srcMIME);
1835
1836 }
1837
1838 CATCH_XMP ("DocOpsUpdateMetadata", false)
1839
1840 }
1841
1842 /*****************************************************************************/
1843
1844 #endif
1845
1846 /*****************************************************************************/
1847