1 /*
2  * exempi - exempi.cpp
3  *
4  * Copyright (C) 2007-2019 Hubert Figuière
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1 Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  *
14  * 2 Redistributions in binary form must reproduce the above copyright
15  * notice, this list of conditions and the following disclaimer in the
16  * documentation and/or other materials provided with the
17  * distribution.
18  *
19  * 3 Neither the name of the Authors, nor the names of its
20  * contributors may be used to endorse or promote products derived
21  * from this software wit hout specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
34  * OF THE POSSIBILITY OF SUCH DAMAGE.
35  */
36 
37 /** @brief this file implement the glue for XMP API
38  */
39 
40 #include "xmpconsts.h"
41 #include "xmp.h"
42 #include "xmperrors.h"
43 
44 #include <string>
45 #include <iostream>
46 #include <memory>
47 
48 #define XMP_INCLUDE_XMPFILES 1
49 #define TXMP_STRING_TYPE std::string
50 #include "XMP.hpp"
51 #include "XMP.incl_cpp"
52 #include "XMPUtils.hpp"
53 
54 /*
55  * Use this to mark a symbol to be exported
56  */
57 #define API_EXPORT __attribute__ ((visibility ("default")))
58 
59 #if HAVE_NATIVE_TLS
60 
61 static TLS int g_error = 0;
62 
set_error(int err)63 static void set_error(int err)
64 {
65     g_error = err;
66 }
67 
68 #else
69 
70 #include <pthread.h>
71 
72 /* Portable thread local storage using pthreads */
73 static pthread_key_t key = NULL;
74 static pthread_once_t key_once = PTHREAD_ONCE_INIT;
75 
76 /* Destructor called when a thread exits - ensure to delete allocated int
77  * pointer */
destroy_tls_key(void * ptr)78 static void destroy_tls_key(void *ptr)
79 {
80     int *err_ptr = static_cast<int *>(ptr);
81     delete err_ptr;
82 }
83 
84 /* Create a key for use with pthreads local storage */
create_tls_key()85 static void create_tls_key()
86 {
87     (void)pthread_key_create(&key, destroy_tls_key);
88 }
89 
90 /* Obtain the latest xmp error for this specific thread - defaults to 0 */
get_error_for_thread()91 static int get_error_for_thread()
92 {
93     int *err_ptr;
94 
95     pthread_once(&key_once, create_tls_key);
96     err_ptr = (int *)pthread_getspecific(key);
97 
98     if (err_ptr == NULL) {
99         return 0;
100     }
101 
102     return *err_ptr;
103 }
104 
105 /* set the current xmp error for this specific thread */
set_error(int err)106 static void set_error(int err)
107 {
108     int *err_ptr;
109 
110     // Ensure that create_thread_local_storage_key is only called
111     // once, by the first thread, to create the key.
112     pthread_once(&key_once, create_tls_key);
113 
114     // Retrieve pointer to int for this thread.
115     err_ptr = (int *)pthread_getspecific(key);
116 
117     // Allocate it, if it does not exists.
118     if (err_ptr == NULL) {
119         err_ptr = new int;
120         pthread_setspecific(key, err_ptr);
121     }
122 
123     // Save the error for this thread.
124     *err_ptr = err;
125 }
126 
127 #endif
128 
set_error(const XMP_Error & e)129 static void set_error(const XMP_Error &e)
130 {
131     set_error(-e.GetID());
132     std::cerr << e.GetErrMsg() << std::endl;
133 }
134 
135 #define RESET_ERROR set_error(0)
136 
137 // Hack: assign between XMP_DateTime and XmpDateTime
138 #define ASSIGN(dst, src)                                                       \
139     (dst).year = (src).year;                                                   \
140     (dst).month = (src).month;                                                 \
141     (dst).day = (src).day;                                                     \
142     (dst).hour = (src).hour;                                                   \
143     (dst).minute = (src).minute;                                               \
144     (dst).second = (src).second;                                               \
145     (dst).tzSign = (src).tzSign;                                               \
146     (dst).tzHour = (src).tzHour;                                               \
147     (dst).tzMinute = (src).tzMinute;                                           \
148     (dst).nanoSecond = (src).nanoSecond;
149 
150 #ifdef __cplusplus
151 extern "C" {
152 #endif
153 
154 API_EXPORT extern const char NS_XMP_META[] = "adobe:ns:meta/";
155 API_EXPORT extern const char NS_RDF[] = kXMP_NS_RDF;
156 API_EXPORT extern const char NS_EXIF[] = kXMP_NS_EXIF;
157 API_EXPORT extern const char NS_TIFF[] = kXMP_NS_TIFF;
158 API_EXPORT extern const char NS_XAP[] = kXMP_NS_XMP;
159 API_EXPORT extern const char NS_XAP_RIGHTS[] = kXMP_NS_XMP_Rights;
160 API_EXPORT extern const char NS_DC[] = kXMP_NS_DC;
161 API_EXPORT extern const char NS_EXIF_AUX[] = kXMP_NS_EXIF_Aux;
162 API_EXPORT extern const char NS_CRS[] = kXMP_NS_CameraRaw;
163 API_EXPORT extern const char NS_LIGHTROOM[] = "http://ns.adobe.com/lightroom/1.0/";
164 API_EXPORT extern const char NS_CAMERA_RAW_SETTINGS[] = kXMP_NS_CameraRaw;
165 API_EXPORT extern const char NS_CAMERA_RAW_SAVED_SETTINGS[] =
166     "http://ns.adobe.com/camera-raw-saved-settings/1.0/";
167 API_EXPORT extern const char NS_PHOTOSHOP[] = kXMP_NS_Photoshop;
168 API_EXPORT extern const char NS_IPTC4XMP[] = kXMP_NS_IPTCCore;
169 API_EXPORT extern const char NS_TPG[] = kXMP_NS_XMP_PagedFile;
170 API_EXPORT extern const char NS_DIMENSIONS_TYPE[] = kXMP_NS_XMP_Dimensions;
171 API_EXPORT extern const char NS_CC[] = "http://creativecommons.org/ns#";
172 API_EXPORT extern const char NS_PDF[] = kXMP_NS_PDF;
173 API_EXPORT extern const char NS_XML[] = kXMP_NS_XML;
174 
175 #define STRING(x) reinterpret_cast<std::string *>(x)
176 
177 #define CHECK_PTR(p, r)                                                        \
178     if (p == NULL) {                                                           \
179         set_error(XMPErr_BadObject);                                           \
180         return r;                                                              \
181     }
182 
183 API_EXPORT
xmp_get_error()184 int xmp_get_error()
185 {
186 #if HAVE_NATIVE_TLS
187     return g_error;
188 #else
189     return get_error_for_thread();
190 #endif
191 }
192 
193 // the error callback - force throwing exceptions
_xmp_error_callback(void *,XMP_ErrorSeverity,XMP_Int32,XMP_StringPtr)194 static bool _xmp_error_callback(void* /*context*/, XMP_ErrorSeverity /*severity*/,
195                                 XMP_Int32 /*cause*/, XMP_StringPtr /*message*/)
196 {
197     return false;
198 }
199 
200 API_EXPORT
xmp_init()201 bool xmp_init()
202 {
203     RESET_ERROR;
204     try {
205         // no need to initialize anything else.
206         // XMP SDK 5.1.2 needs this because it has been stripped off local
207         // text conversion the one that was done in Exempi with libiconv.
208         bool result = SXMPFiles::Initialize(kXMPFiles_IgnoreLocalText);
209         SXMPMeta::SetDefaultErrorCallback(&_xmp_error_callback, nullptr, 1);
210         return result;
211     }
212     catch (const XMP_Error &e) {
213         set_error(e);
214     }
215     return false;
216 }
217 
218 API_EXPORT
xmp_terminate()219 void xmp_terminate()
220 {
221     RESET_ERROR;
222     SXMPFiles::Terminate();
223 }
224 
225 API_EXPORT
xmp_register_namespace(const char * namespaceURI,const char * suggestedPrefix,XmpStringPtr registeredPrefix)226 bool xmp_register_namespace(const char *namespaceURI,
227                             const char *suggestedPrefix,
228                             XmpStringPtr registeredPrefix)
229 {
230     RESET_ERROR;
231     try {
232         return SXMPMeta::RegisterNamespace(namespaceURI, suggestedPrefix,
233                                            STRING(registeredPrefix));
234     }
235     catch (const XMP_Error &e) {
236         set_error(e);
237     }
238     return false;
239 }
240 
241 API_EXPORT
xmp_namespace_prefix(const char * ns,XmpStringPtr prefix)242 bool xmp_namespace_prefix(const char *ns, XmpStringPtr prefix)
243 {
244     CHECK_PTR(ns, false);
245     RESET_ERROR;
246     try {
247         return SXMPMeta::GetNamespacePrefix(ns, STRING(prefix));
248     }
249     catch (const XMP_Error &e) {
250         set_error(e);
251     }
252     return false;
253 }
254 
255 API_EXPORT
xmp_prefix_namespace_uri(const char * prefix,XmpStringPtr ns)256 bool xmp_prefix_namespace_uri(const char *prefix, XmpStringPtr ns)
257 {
258     CHECK_PTR(prefix, false);
259     RESET_ERROR;
260     try {
261         return SXMPMeta::GetNamespaceURI(prefix, STRING(ns));
262     }
263     catch (const XMP_Error &e) {
264         set_error(e);
265     }
266     return false;
267 }
268 
269 API_EXPORT
xmp_files_new()270 XmpFilePtr xmp_files_new()
271 {
272     RESET_ERROR;
273 
274     try {
275         auto txf = std::unique_ptr<SXMPFiles>(new SXMPFiles());
276 
277         return reinterpret_cast<XmpFilePtr>(txf.release());
278     }
279     catch (const XMP_Error &e) {
280         set_error(e);
281     }
282     return NULL;
283 }
284 
285 API_EXPORT
xmp_files_open_new(const char * path,XmpOpenFileOptions options)286 XmpFilePtr xmp_files_open_new(const char *path, XmpOpenFileOptions options)
287 {
288     CHECK_PTR(path, NULL);
289     RESET_ERROR;
290 
291     try {
292         auto txf = std::unique_ptr<SXMPFiles>(new SXMPFiles);
293 
294         txf->OpenFile(path, XMP_FT_UNKNOWN, options);
295 
296         return reinterpret_cast<XmpFilePtr>(txf.release());
297     }
298     catch (const XMP_Error &e) {
299         set_error(e);
300     }
301 
302     return NULL;
303 }
304 
305 API_EXPORT
xmp_files_open(XmpFilePtr xf,const char * path,XmpOpenFileOptions options)306 bool xmp_files_open(XmpFilePtr xf, const char *path, XmpOpenFileOptions options)
307 {
308     CHECK_PTR(xf, false);
309     RESET_ERROR;
310     auto txf = reinterpret_cast<SXMPFiles *>(xf);
311     try {
312         return txf->OpenFile(path, XMP_FT_UNKNOWN, options);
313     }
314     catch (const XMP_Error &e) {
315         set_error(e);
316     }
317     return false;
318 }
319 
320 API_EXPORT
xmp_files_close(XmpFilePtr xf,XmpCloseFileOptions options)321 bool xmp_files_close(XmpFilePtr xf, XmpCloseFileOptions options)
322 {
323     CHECK_PTR(xf, false);
324     RESET_ERROR;
325     try {
326         auto txf = reinterpret_cast<SXMPFiles *>(xf);
327         txf->CloseFile(options);
328     }
329     catch (const XMP_Error &e) {
330         set_error(e);
331         return false;
332     }
333     return true;
334 }
335 
336 API_EXPORT
xmp_files_get_new_xmp(XmpFilePtr xf)337 XmpPtr xmp_files_get_new_xmp(XmpFilePtr xf)
338 {
339     CHECK_PTR(xf, NULL);
340     RESET_ERROR;
341     auto txf = reinterpret_cast<SXMPFiles *>(xf);
342 
343     bool result = false;
344     try {
345         auto xmp = std::unique_ptr<SXMPMeta>(new SXMPMeta);
346         result = txf->GetXMP(xmp.get());
347         if (result) {
348             return reinterpret_cast<XmpPtr>(xmp.release());
349         }
350     }
351     catch (const XMP_Error &e) {
352         set_error(e);
353     }
354     return NULL;
355 }
356 
357 API_EXPORT
xmp_files_get_xmp(XmpFilePtr xf,XmpPtr xmp)358 bool xmp_files_get_xmp(XmpFilePtr xf, XmpPtr xmp)
359 {
360     CHECK_PTR(xf, false);
361     CHECK_PTR(xmp, false);
362     RESET_ERROR;
363     bool result = false;
364     try {
365         auto txf = reinterpret_cast<SXMPFiles *>(xf);
366 
367         result = txf->GetXMP(reinterpret_cast<SXMPMeta *>(xmp));
368     }
369     catch (const XMP_Error &e) {
370         set_error(e);
371     }
372     return result;
373 }
374 
375 API_EXPORT
xmp_files_get_xmp_xmpstring(XmpFilePtr xf,XmpStringPtr xmp_packet,XmpPacketInfo * packet_info)376 bool xmp_files_get_xmp_xmpstring(XmpFilePtr xf, XmpStringPtr xmp_packet,
377                                  XmpPacketInfo* packet_info)
378 {
379   CHECK_PTR(xf, false);
380   CHECK_PTR(xmp_packet, false);
381   RESET_ERROR;
382   bool result = false;
383   try {
384     SXMPFiles *txf = reinterpret_cast<SXMPFiles*>(xf);
385 
386     XMP_PacketInfo xmp_packet_info;
387     result = txf->GetXMP(NULL,
388                          reinterpret_cast<std::string*>(xmp_packet),
389                          &xmp_packet_info);
390     if (packet_info) {
391       packet_info->offset = xmp_packet_info.offset;
392       packet_info->length = xmp_packet_info.length;
393       packet_info->padSize = xmp_packet_info.padSize;
394       packet_info->charForm = xmp_packet_info.charForm;
395       packet_info->writeable = xmp_packet_info.writeable;
396       packet_info->hasWrapper = xmp_packet_info.hasWrapper;
397       packet_info->pad = xmp_packet_info.pad;
398     }
399   }
400   catch(const XMP_Error & e) {
401     set_error(e);
402   }
403   return result;
404 }
405 
406 
407 API_EXPORT
xmp_files_can_put_xmp(XmpFilePtr xf,XmpPtr xmp)408 bool xmp_files_can_put_xmp(XmpFilePtr xf, XmpPtr xmp)
409 {
410     CHECK_PTR(xf, false);
411     RESET_ERROR;
412     auto txf = reinterpret_cast<SXMPFiles *>(xf);
413     bool result = false;
414 
415     try {
416         result = txf->CanPutXMP(*reinterpret_cast<const SXMPMeta *>(xmp));
417     }
418     catch (const XMP_Error &e) {
419         set_error(e);
420         return false;
421     }
422     return result;
423 }
424 
425 API_EXPORT
xmp_files_can_put_xmp_xmpstring(XmpFilePtr xf,XmpStringPtr xmp_packet)426 bool xmp_files_can_put_xmp_xmpstring(XmpFilePtr xf, XmpStringPtr xmp_packet)
427 {
428   CHECK_PTR(xf, false);
429   RESET_ERROR;
430   SXMPFiles *txf = reinterpret_cast<SXMPFiles*>(xf);
431   bool result = false;
432 
433   try {
434     result = txf->CanPutXMP(*reinterpret_cast<const std::string*>(xmp_packet));
435   }
436   catch(const XMP_Error & e) {
437     set_error(e);
438     return false;
439   }
440   return result;
441 }
442 
443 API_EXPORT
xmp_files_can_put_xmp_cstr(XmpFilePtr xf,const char * xmp_packet,size_t len)444 bool xmp_files_can_put_xmp_cstr(XmpFilePtr xf, const char* xmp_packet, size_t len)
445 {
446   CHECK_PTR(xf, false);
447   RESET_ERROR;
448   SXMPFiles *txf = reinterpret_cast<SXMPFiles*>(xf);
449   bool result = false;
450 
451   try {
452     result = txf->CanPutXMP(xmp_packet, len);
453   }
454   catch(const XMP_Error & e) {
455     set_error(e);
456     return false;
457   }
458   return result;
459 }
460 
461 API_EXPORT
xmp_files_put_xmp(XmpFilePtr xf,XmpPtr xmp)462 bool xmp_files_put_xmp(XmpFilePtr xf, XmpPtr xmp)
463 {
464     CHECK_PTR(xf, false);
465     CHECK_PTR(xmp, false);
466     RESET_ERROR;
467     auto txf = reinterpret_cast<SXMPFiles *>(xf);
468 
469     try {
470         txf->PutXMP(*reinterpret_cast<const SXMPMeta *>(xmp));
471     }
472     catch (const XMP_Error &e) {
473         set_error(e);
474         return false;
475     }
476     return true;
477 }
478 
479 API_EXPORT
xmp_files_put_xmp_xmpstring(XmpFilePtr xf,XmpStringPtr xmp_packet)480 bool xmp_files_put_xmp_xmpstring(XmpFilePtr xf, XmpStringPtr xmp_packet)
481 {
482   CHECK_PTR(xf, false);
483   CHECK_PTR(xmp_packet, false);
484   RESET_ERROR;
485   SXMPFiles *txf = reinterpret_cast<SXMPFiles*>(xf);
486 
487   try {
488     txf->PutXMP(*reinterpret_cast<const std::string*>(xmp_packet));
489   }
490   catch(const XMP_Error & e) {
491     set_error(e);
492     return false;
493   }
494   return true;
495 }
496 
497 API_EXPORT
xmp_files_put_xmp_cstr(XmpFilePtr xf,const char * xmp_packet,size_t len)498 bool xmp_files_put_xmp_cstr(XmpFilePtr xf, const char* xmp_packet, size_t len)
499 {
500   CHECK_PTR(xf, false);
501   CHECK_PTR(xmp_packet, false);
502   RESET_ERROR;
503   SXMPFiles *txf = reinterpret_cast<SXMPFiles*>(xf);
504 
505   try {
506     txf->PutXMP(xmp_packet, len);
507   }
508   catch(const XMP_Error & e) {
509     set_error(e);
510     return false;
511   }
512   return true;
513 }
514 
515 API_EXPORT
xmp_files_get_file_info(XmpFilePtr xf,XmpStringPtr filePath,XmpOpenFileOptions * options,XmpFileType * file_format,XmpFileFormatOptions * handler_flags)516 bool xmp_files_get_file_info(XmpFilePtr xf, XmpStringPtr filePath,
517                              XmpOpenFileOptions *options,
518                              XmpFileType *file_format,
519                              XmpFileFormatOptions *handler_flags)
520 {
521     CHECK_PTR(xf, false);
522     RESET_ERROR;
523 
524     bool result = false;
525     auto txf = reinterpret_cast<SXMPFiles *>(xf);
526     try {
527         result = txf->GetFileInfo(STRING(filePath), (XMP_OptionBits *)options,
528                                   (XMP_FileFormat *)file_format,
529                                   (XMP_OptionBits *)handler_flags);
530     }
531     catch (const XMP_Error &e) {
532         set_error(e);
533         return false;
534     }
535 
536     return result;
537 }
538 
539 API_EXPORT
xmp_files_free(XmpFilePtr xf)540 bool xmp_files_free(XmpFilePtr xf)
541 {
542     CHECK_PTR(xf, false);
543     RESET_ERROR;
544     auto txf = reinterpret_cast<SXMPFiles *>(xf);
545     try {
546         delete txf;
547     }
548     catch (const XMP_Error &e) {
549         set_error(e);
550         return false;
551     }
552     return true;
553 }
554 
555 API_EXPORT
xmp_files_get_format_info(XmpFileType format,XmpFileFormatOptions * options)556 bool xmp_files_get_format_info(XmpFileType format,
557                                XmpFileFormatOptions *options)
558 {
559     RESET_ERROR;
560 
561     bool result = false;
562     try {
563         result = SXMPFiles::GetFormatInfo(format, (XMP_OptionBits *)options);
564     }
565     catch (const XMP_Error &e) {
566         set_error(e);
567         return false;
568     }
569     return result;
570 }
571 
572 API_EXPORT
xmp_files_check_file_format(const char * filePath)573 XmpFileType xmp_files_check_file_format(const char *filePath)
574 {
575     CHECK_PTR(filePath, XMP_FT_UNKNOWN);
576     RESET_ERROR;
577 
578     XmpFileType file_type = XMP_FT_UNKNOWN;
579     try {
580         file_type = (XmpFileType)SXMPFiles::CheckFileFormat(filePath);
581     }
582     catch (const XMP_Error &e) {
583         set_error(e);
584         return XMP_FT_UNKNOWN;
585     }
586     return file_type;
587 }
588 
589 API_EXPORT
xmp_new_empty()590 XmpPtr xmp_new_empty()
591 {
592     RESET_ERROR;
593     SXMPMeta *txmp = new SXMPMeta;
594     return (XmpPtr)txmp;
595 }
596 
597 API_EXPORT
xmp_new(const char * buffer,size_t len)598 XmpPtr xmp_new(const char *buffer, size_t len)
599 {
600     CHECK_PTR(buffer, NULL);
601     RESET_ERROR;
602 
603     try {
604         auto txmp = std::unique_ptr<SXMPMeta>(new SXMPMeta(buffer, len));
605         return reinterpret_cast<XmpPtr>(txmp.release());
606     }
607     catch (const XMP_Error &e) {
608         set_error(e);
609     }
610     return NULL;
611 }
612 
613 API_EXPORT
xmp_copy(XmpPtr xmp)614 XmpPtr xmp_copy(XmpPtr xmp)
615 {
616     CHECK_PTR(xmp, NULL);
617     RESET_ERROR;
618 
619     try {
620         auto txmp = std::unique_ptr<SXMPMeta>(new SXMPMeta(*(SXMPMeta *)xmp));
621         return reinterpret_cast<XmpPtr>(txmp.release());
622     }
623     catch (const XMP_Error &e) {
624         set_error(e);
625     }
626     return NULL;
627 }
628 
629 API_EXPORT
xmp_parse(XmpPtr xmp,const char * buffer,size_t len)630 bool xmp_parse(XmpPtr xmp, const char *buffer, size_t len)
631 {
632     CHECK_PTR(xmp, false);
633     CHECK_PTR(buffer, false);
634 
635     SXMPMeta *txmp = (SXMPMeta *)xmp;
636     try {
637         txmp->ParseFromBuffer(buffer, len, kXMP_RequireXMPMeta);
638     }
639     catch (const XMP_Error &e) {
640         set_error(e);
641         return false;
642     }
643     return true;
644 }
645 
646 API_EXPORT
xmp_serialize(XmpPtr xmp,XmpStringPtr buffer,uint32_t options,uint32_t padding)647 bool xmp_serialize(XmpPtr xmp, XmpStringPtr buffer, uint32_t options,
648                    uint32_t padding)
649 {
650     RESET_ERROR;
651     return xmp_serialize_and_format(xmp, buffer, options, padding, "\n", " ",
652                                     0);
653 }
654 
655 API_EXPORT
xmp_serialize_and_format(XmpPtr xmp,XmpStringPtr buffer,uint32_t options,uint32_t padding,const char * newline,const char * tab,int32_t indent)656 bool xmp_serialize_and_format(XmpPtr xmp, XmpStringPtr buffer, uint32_t options,
657                               uint32_t padding, const char *newline,
658                               const char *tab, int32_t indent)
659 {
660     CHECK_PTR(xmp, false);
661     CHECK_PTR(buffer, false);
662     RESET_ERROR;
663 
664     SXMPMeta *txmp = (SXMPMeta *)xmp;
665     try {
666         txmp->SerializeToBuffer(STRING(buffer), options, padding, newline, tab,
667                                 indent);
668     }
669     catch (const XMP_Error &e) {
670         set_error(e);
671         return false;
672     }
673     return true;
674 }
675 
676 API_EXPORT
xmp_free(XmpPtr xmp)677 bool xmp_free(XmpPtr xmp)
678 {
679     CHECK_PTR(xmp, false);
680     RESET_ERROR;
681     SXMPMeta *txmp = (SXMPMeta *)xmp;
682     delete txmp;
683     return true;
684 }
685 
686 API_EXPORT
xmp_get_property(XmpPtr xmp,const char * schema,const char * name,XmpStringPtr property,uint32_t * propsBits)687 bool xmp_get_property(XmpPtr xmp, const char *schema, const char *name,
688                       XmpStringPtr property, uint32_t *propsBits)
689 {
690     CHECK_PTR(xmp, false);
691     RESET_ERROR;
692 
693     bool ret = false;
694     try {
695         auto txmp = reinterpret_cast<const SXMPMeta *>(xmp);
696         XMP_OptionBits optionBits;
697         ret = txmp->GetProperty(schema, name, STRING(property), &optionBits);
698         if (propsBits) {
699             *propsBits = optionBits;
700         }
701     }
702     catch (const XMP_Error &e) {
703         set_error(e);
704     }
705     return ret;
706 }
707 
708 API_EXPORT
xmp_get_property_date(XmpPtr xmp,const char * schema,const char * name,XmpDateTime * property,uint32_t * propsBits)709 bool xmp_get_property_date(XmpPtr xmp, const char *schema, const char *name,
710                            XmpDateTime *property, uint32_t *propsBits)
711 {
712     CHECK_PTR(xmp, false);
713     RESET_ERROR;
714 
715     bool ret = false;
716     try {
717         auto txmp = reinterpret_cast<const SXMPMeta *>(xmp);
718         XMP_OptionBits optionBits;
719         XMP_DateTime dt;
720         //		memset((void*)&dt, 1, sizeof(XMP_DateTime));
721         ret = txmp->GetProperty_Date(schema, name, &dt, &optionBits);
722         ASSIGN((*property), dt);
723         if (propsBits) {
724             *propsBits = optionBits;
725         }
726     }
727     catch (const XMP_Error &e) {
728         set_error(e);
729     }
730     return ret;
731 }
732 
733 API_EXPORT
xmp_get_property_float(XmpPtr xmp,const char * schema,const char * name,double * property,uint32_t * propsBits)734 bool xmp_get_property_float(XmpPtr xmp, const char *schema, const char *name,
735                             double *property, uint32_t *propsBits)
736 {
737     CHECK_PTR(xmp, false);
738     RESET_ERROR;
739 
740     bool ret = false;
741     try {
742         auto txmp = reinterpret_cast<const SXMPMeta *>(xmp);
743         XMP_OptionBits optionBits;
744         ret = txmp->GetProperty_Float(schema, name, property, &optionBits);
745         if (propsBits) {
746             *propsBits = optionBits;
747         }
748     }
749     catch (const XMP_Error &e) {
750         set_error(e);
751     }
752     return ret;
753 }
754 
755 API_EXPORT
xmp_get_property_bool(XmpPtr xmp,const char * schema,const char * name,bool * property,uint32_t * propsBits)756 bool xmp_get_property_bool(XmpPtr xmp, const char *schema, const char *name,
757                            bool *property, uint32_t *propsBits)
758 {
759     CHECK_PTR(xmp, false);
760     RESET_ERROR;
761 
762     bool ret = false;
763     try {
764         auto txmp = reinterpret_cast<const SXMPMeta *>(xmp);
765         XMP_OptionBits optionBits;
766         ret = txmp->GetProperty_Bool(schema, name, property, &optionBits);
767         if (propsBits) {
768             *propsBits = optionBits;
769         }
770     }
771     catch (const XMP_Error &e) {
772         set_error(e);
773     }
774     return ret;
775 }
776 
777 API_EXPORT
xmp_get_property_int32(XmpPtr xmp,const char * schema,const char * name,int32_t * property,uint32_t * propsBits)778 bool xmp_get_property_int32(XmpPtr xmp, const char *schema, const char *name,
779                             int32_t *property, uint32_t *propsBits)
780 {
781     CHECK_PTR(xmp, false);
782     RESET_ERROR;
783 
784     bool ret = false;
785     try {
786         auto txmp = reinterpret_cast<const SXMPMeta *>(xmp);
787         XMP_OptionBits optionBits;
788         // the long converstion is needed until XMPCore is fixed it use proper
789         // types.
790         ret = txmp->GetProperty_Int(schema, name, property, &optionBits);
791         if (propsBits) {
792             *propsBits = optionBits;
793         }
794     }
795     catch (const XMP_Error &e) {
796         set_error(e);
797     }
798     return ret;
799 }
800 
801 API_EXPORT
xmp_get_property_int64(XmpPtr xmp,const char * schema,const char * name,int64_t * property,uint32_t * propsBits)802 bool xmp_get_property_int64(XmpPtr xmp, const char *schema, const char *name,
803                             int64_t *property, uint32_t *propsBits)
804 {
805     CHECK_PTR(xmp, false);
806     RESET_ERROR;
807 
808     bool ret = false;
809     try {
810         auto txmp = reinterpret_cast<const SXMPMeta *>(xmp);
811         XMP_OptionBits optionBits;
812         ret = txmp->GetProperty_Int64(schema, name, property, &optionBits);
813         if (propsBits) {
814             *propsBits = optionBits;
815         }
816     }
817     catch (const XMP_Error &e) {
818         set_error(e);
819     }
820     return ret;
821 }
822 
823 API_EXPORT
xmp_get_array_item(XmpPtr xmp,const char * schema,const char * name,int32_t index,XmpStringPtr property,uint32_t * propsBits)824 bool xmp_get_array_item(XmpPtr xmp, const char *schema, const char *name,
825                         int32_t index, XmpStringPtr property,
826                         uint32_t *propsBits)
827 {
828     CHECK_PTR(xmp, false);
829     RESET_ERROR;
830 
831     bool ret = false;
832     try {
833         auto txmp = reinterpret_cast<const SXMPMeta *>(xmp);
834         XMP_OptionBits optionBits;
835         ret = txmp->GetArrayItem(schema, name, index, STRING(property),
836                                  &optionBits);
837         if (propsBits) {
838             *propsBits = optionBits;
839         }
840     }
841     catch (const XMP_Error &e) {
842         set_error(e);
843     }
844     return ret;
845 }
846 
847 API_EXPORT
xmp_set_property(XmpPtr xmp,const char * schema,const char * name,const char * value,uint32_t optionBits)848 bool xmp_set_property(XmpPtr xmp, const char *schema, const char *name,
849                       const char *value, uint32_t optionBits)
850 {
851     CHECK_PTR(xmp, false);
852     RESET_ERROR;
853 
854     bool ret = false;
855     auto txmp = reinterpret_cast<SXMPMeta *>(xmp);
856     // see bug #16030
857     // when it is a struct or an array, get prop return an empty string
858     // but it fail if passed an empty string
859     if ((optionBits & (XMP_PROP_VALUE_IS_STRUCT | XMP_PROP_VALUE_IS_ARRAY)) &&
860         (*value == 0)) {
861         value = NULL;
862     }
863     try {
864         txmp->SetProperty(schema, name, value, optionBits);
865         ret = true;
866     }
867     catch (const XMP_Error &e) {
868         set_error(e);
869     }
870     catch (...) {
871     }
872     return ret;
873 }
874 
875 API_EXPORT
xmp_set_property_date(XmpPtr xmp,const char * schema,const char * name,const XmpDateTime * value,uint32_t optionBits)876 bool xmp_set_property_date(XmpPtr xmp, const char *schema, const char *name,
877                            const XmpDateTime *value, uint32_t optionBits)
878 {
879     CHECK_PTR(xmp, false);
880     RESET_ERROR;
881 
882     bool ret = false;
883     auto txmp = reinterpret_cast<SXMPMeta *>(xmp);
884     try {
885         XMP_DateTime dt;
886         ASSIGN(dt, (*value));
887         txmp->SetProperty_Date(schema, name, dt, optionBits);
888         ret = true;
889     }
890     catch (const XMP_Error &e) {
891         set_error(e);
892     }
893     catch (...) {
894     }
895     return ret;
896 }
897 
898 API_EXPORT
xmp_set_property_float(XmpPtr xmp,const char * schema,const char * name,double value,uint32_t optionBits)899 bool xmp_set_property_float(XmpPtr xmp, const char *schema, const char *name,
900                             double value, uint32_t optionBits)
901 {
902     CHECK_PTR(xmp, false);
903     RESET_ERROR;
904 
905     bool ret = false;
906     auto txmp = reinterpret_cast<SXMPMeta *>(xmp);
907     try {
908         txmp->SetProperty_Float(schema, name, value, optionBits);
909         ret = true;
910     }
911     catch (const XMP_Error &e) {
912         set_error(e);
913     }
914     catch (...) {
915     }
916     return ret;
917 }
918 
919 API_EXPORT
xmp_set_property_bool(XmpPtr xmp,const char * schema,const char * name,bool value,uint32_t optionBits)920 bool xmp_set_property_bool(XmpPtr xmp, const char *schema, const char *name,
921                            bool value, uint32_t optionBits)
922 {
923     CHECK_PTR(xmp, false);
924     RESET_ERROR;
925 
926     bool ret = false;
927     auto txmp = reinterpret_cast<SXMPMeta *>(xmp);
928     try {
929         txmp->SetProperty_Bool(schema, name, value, optionBits);
930         ret = true;
931     }
932     catch (const XMP_Error &e) {
933         set_error(e);
934     }
935     catch (...) {
936     }
937     return ret;
938 }
939 
940 API_EXPORT
xmp_set_property_int32(XmpPtr xmp,const char * schema,const char * name,int32_t value,uint32_t optionBits)941 bool xmp_set_property_int32(XmpPtr xmp, const char *schema, const char *name,
942                             int32_t value, uint32_t optionBits)
943 {
944     CHECK_PTR(xmp, false);
945     RESET_ERROR;
946 
947     bool ret = false;
948     auto txmp = reinterpret_cast<SXMPMeta *>(xmp);
949     try {
950         txmp->SetProperty_Int(schema, name, value, optionBits);
951         ret = true;
952     }
953     catch (const XMP_Error &e) {
954         set_error(e);
955     }
956     catch (...) {
957     }
958     return ret;
959 }
960 
961 API_EXPORT
xmp_set_property_int64(XmpPtr xmp,const char * schema,const char * name,int64_t value,uint32_t optionBits)962 bool xmp_set_property_int64(XmpPtr xmp, const char *schema, const char *name,
963                             int64_t value, uint32_t optionBits)
964 {
965     CHECK_PTR(xmp, false);
966     RESET_ERROR;
967 
968     bool ret = false;
969     auto txmp = reinterpret_cast<SXMPMeta *>(xmp);
970     try {
971         txmp->SetProperty_Int64(schema, name, value, optionBits);
972         ret = true;
973     }
974     catch (const XMP_Error &e) {
975         set_error(e);
976     }
977     catch (...) {
978     }
979     return ret;
980 }
981 
982 API_EXPORT
xmp_set_array_item(XmpPtr xmp,const char * schema,const char * name,int32_t index,const char * value,uint32_t optionBits)983 bool xmp_set_array_item(XmpPtr xmp, const char *schema, const char *name,
984                         int32_t index, const char *value, uint32_t optionBits)
985 {
986     CHECK_PTR(xmp, false);
987     RESET_ERROR;
988 
989     bool ret = false;
990     auto txmp = reinterpret_cast<SXMPMeta *>(xmp);
991     try {
992         txmp->SetArrayItem(schema, name, index, value, optionBits);
993         ret = true;
994     }
995     catch (const XMP_Error &e) {
996         set_error(e);
997     }
998     catch (...) {
999     }
1000     return ret;
1001 }
1002 
1003 API_EXPORT
xmp_append_array_item(XmpPtr xmp,const char * schema,const char * name,uint32_t arrayOptions,const char * value,uint32_t optionBits)1004 bool xmp_append_array_item(XmpPtr xmp, const char *schema, const char *name,
1005                            uint32_t arrayOptions, const char *value,
1006                            uint32_t optionBits)
1007 {
1008     CHECK_PTR(xmp, false);
1009     RESET_ERROR;
1010 
1011     bool ret = false;
1012     auto txmp = reinterpret_cast<SXMPMeta *>(xmp);
1013     try {
1014         txmp->AppendArrayItem(schema, name, arrayOptions, value, optionBits);
1015         ret = true;
1016     }
1017     catch (const XMP_Error &e) {
1018         set_error(e);
1019     }
1020     catch (...) {
1021     }
1022     return ret;
1023 }
1024 
1025 API_EXPORT
xmp_delete_property(XmpPtr xmp,const char * schema,const char * name)1026 bool xmp_delete_property(XmpPtr xmp, const char *schema, const char *name)
1027 {
1028     CHECK_PTR(xmp, false);
1029     RESET_ERROR;
1030 
1031     bool ret = true;
1032     auto txmp = reinterpret_cast<SXMPMeta *>(xmp);
1033     try {
1034         txmp->DeleteProperty(schema, name);
1035     }
1036     catch (const XMP_Error &e) {
1037         set_error(e);
1038         ret = false;
1039     }
1040     catch (...) {
1041         ret = false;
1042     }
1043     return ret;
1044 }
1045 
1046 API_EXPORT
xmp_has_property(XmpPtr xmp,const char * schema,const char * name)1047 bool xmp_has_property(XmpPtr xmp, const char *schema, const char *name)
1048 {
1049     CHECK_PTR(xmp, false);
1050     RESET_ERROR;
1051 
1052     bool ret = true;
1053     auto txmp = reinterpret_cast<const SXMPMeta *>(xmp);
1054     try {
1055         ret = txmp->DoesPropertyExist(schema, name);
1056     }
1057     catch (const XMP_Error &e) {
1058         set_error(e);
1059         ret = false;
1060     }
1061     catch (...) {
1062         ret = false;
1063     }
1064     return ret;
1065 }
1066 
1067 API_EXPORT
xmp_get_localized_text(XmpPtr xmp,const char * schema,const char * name,const char * genericLang,const char * specificLang,XmpStringPtr actualLang,XmpStringPtr itemValue,uint32_t * propsBits)1068 bool xmp_get_localized_text(XmpPtr xmp, const char *schema, const char *name,
1069                             const char *genericLang, const char *specificLang,
1070                             XmpStringPtr actualLang, XmpStringPtr itemValue,
1071                             uint32_t *propsBits)
1072 {
1073     CHECK_PTR(xmp, false);
1074     RESET_ERROR;
1075 
1076     bool ret = false;
1077     try {
1078         auto txmp = reinterpret_cast<const SXMPMeta *>(xmp);
1079         XMP_OptionBits optionBits;
1080         ret = txmp->GetLocalizedText(schema, name, genericLang, specificLang,
1081                                      STRING(actualLang), STRING(itemValue),
1082                                      &optionBits);
1083         if (propsBits) {
1084             *propsBits = optionBits;
1085         }
1086     }
1087     catch (const XMP_Error &e) {
1088         set_error(e);
1089         ret = false;
1090     }
1091     return ret;
1092 }
1093 
1094 API_EXPORT
xmp_set_localized_text(XmpPtr xmp,const char * schema,const char * name,const char * genericLang,const char * specificLang,const char * value,uint32_t optionBits)1095 bool xmp_set_localized_text(XmpPtr xmp, const char *schema, const char *name,
1096                             const char *genericLang, const char *specificLang,
1097                             const char *value, uint32_t optionBits)
1098 {
1099     CHECK_PTR(xmp, false);
1100     RESET_ERROR;
1101 
1102     bool ret = true;
1103     auto txmp = reinterpret_cast<SXMPMeta *>(xmp);
1104     try {
1105         txmp->SetLocalizedText(schema, name, genericLang, specificLang, value,
1106                                optionBits);
1107     }
1108     catch (const XMP_Error &e) {
1109         set_error(e);
1110         ret = false;
1111     }
1112     catch (...) {
1113         ret = false;
1114     }
1115     return ret;
1116 }
1117 
1118 API_EXPORT
xmp_delete_localized_text(XmpPtr xmp,const char * schema,const char * name,const char * genericLang,const char * specificLang)1119 bool xmp_delete_localized_text(XmpPtr xmp, const char *schema, const char *name,
1120                                const char *genericLang,
1121                                const char *specificLang)
1122 {
1123     CHECK_PTR(xmp, false);
1124     RESET_ERROR;
1125 
1126     bool ret = true;
1127     auto txmp = reinterpret_cast<SXMPMeta *>(xmp);
1128     try {
1129         txmp->DeleteLocalizedText(schema, name, genericLang, specificLang);
1130     }
1131     catch (const XMP_Error &e) {
1132         set_error(e);
1133         ret = false;
1134     }
1135     catch (...) {
1136         ret = false;
1137     }
1138     return ret;
1139 }
1140 
1141 API_EXPORT
xmp_string_new()1142 XmpStringPtr xmp_string_new()
1143 {
1144     return (XmpStringPtr) new std::string;
1145 }
1146 
1147 API_EXPORT
xmp_string_free(XmpStringPtr s)1148 void xmp_string_free(XmpStringPtr s)
1149 {
1150     auto str = reinterpret_cast<std::string *>(s);
1151     delete str;
1152 }
1153 
1154 API_EXPORT
xmp_string_cstr(XmpStringPtr s)1155 const char *xmp_string_cstr(XmpStringPtr s)
1156 {
1157     CHECK_PTR(s, NULL);
1158     return reinterpret_cast<const std::string *>(s)->c_str();
1159 }
1160 
1161 API_EXPORT
xmp_string_len(XmpStringPtr s)1162 size_t xmp_string_len(XmpStringPtr s)
1163 {
1164     CHECK_PTR(s, 0);
1165     return reinterpret_cast<const std::string *>(s)->size();
1166 }
1167 
1168 API_EXPORT
xmp_iterator_new(XmpPtr xmp,const char * schema,const char * propName,XmpIterOptions options)1169 XmpIteratorPtr xmp_iterator_new(XmpPtr xmp, const char *schema,
1170                                 const char *propName, XmpIterOptions options)
1171 {
1172     CHECK_PTR(xmp, NULL);
1173     RESET_ERROR;
1174 
1175     try {
1176         auto xiter = std::unique_ptr<SXMPIterator>(
1177             new SXMPIterator(*(SXMPMeta *)xmp, schema, propName, options));
1178 
1179         return reinterpret_cast<XmpIteratorPtr>(xiter.release());
1180     }
1181     catch (const XMP_Error &e) {
1182         set_error(e);
1183     }
1184 
1185     return NULL;
1186 }
1187 
1188 API_EXPORT
xmp_iterator_free(XmpIteratorPtr iter)1189 bool xmp_iterator_free(XmpIteratorPtr iter)
1190 {
1191     CHECK_PTR(iter, false);
1192     RESET_ERROR;
1193     auto titer = reinterpret_cast<SXMPIterator *>(iter);
1194     delete titer;
1195     return true;
1196 }
1197 
1198 API_EXPORT
xmp_iterator_next(XmpIteratorPtr iter,XmpStringPtr schema,XmpStringPtr propName,XmpStringPtr propValue,uint32_t * options)1199 bool xmp_iterator_next(XmpIteratorPtr iter, XmpStringPtr schema,
1200                        XmpStringPtr propName, XmpStringPtr propValue,
1201                        uint32_t *options)
1202 {
1203     CHECK_PTR(iter, false);
1204     RESET_ERROR;
1205     auto titer = reinterpret_cast<SXMPIterator *>(iter);
1206     return titer->Next(reinterpret_cast<std::string *>(schema),
1207                        reinterpret_cast<std::string *>(propName),
1208                        reinterpret_cast<std::string *>(propValue), options);
1209 }
1210 
1211 API_EXPORT
xmp_iterator_skip(XmpIteratorPtr iter,XmpIterSkipOptions options)1212 bool xmp_iterator_skip(XmpIteratorPtr iter, XmpIterSkipOptions options)
1213 {
1214     CHECK_PTR(iter, false);
1215     RESET_ERROR;
1216     auto titer = reinterpret_cast<SXMPIterator *>(iter);
1217     titer->Skip(options);
1218     return true;
1219 }
1220 
1221 API_EXPORT
xmp_datetime_compare(XmpDateTime * left,XmpDateTime * right)1222 int xmp_datetime_compare(XmpDateTime *left, XmpDateTime *right)
1223 {
1224     if (!left && !right) {
1225         return 0;
1226     }
1227     if (!left) {
1228         return -1;
1229     }
1230     if (!right) {
1231         return 1;
1232     }
1233     XMP_DateTime _left;
1234     ASSIGN(_left, *left);
1235     XMP_DateTime _right;
1236     ASSIGN(_right, *right);
1237     return XMPUtils::CompareDateTime(_left, _right);
1238 }
1239 
1240 #ifdef __cplusplus
1241 }
1242 #endif
1243