/src/exiv2/include/exiv2/xmp_exiv2.hpp
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | |
3 | | #ifndef EXIV2_XMP_EXIV2_HPP |
4 | | #define EXIV2_XMP_EXIV2_HPP |
5 | | |
6 | | // ***************************************************************************** |
7 | | #include "exiv2lib_export.h" |
8 | | |
9 | | // included header files |
10 | | #include "datasets.hpp" |
11 | | #include "metadatum.hpp" |
12 | | #include "properties.hpp" |
13 | | |
14 | | #include <atomic> |
15 | | #include <map> |
16 | | |
17 | | // ***************************************************************************** |
18 | | // namespace extensions |
19 | | namespace Exiv2 { |
20 | | // ***************************************************************************** |
21 | | // class declarations |
22 | | class ExifData; |
23 | | class XmpKey; |
24 | | |
25 | | // ***************************************************************************** |
26 | | // class definitions |
27 | | |
28 | | /*! |
29 | | @brief Information related to an XMP property. An XMP metadatum consists |
30 | | of an XmpKey and a Value and provides methods to manipulate these. |
31 | | */ |
32 | | class EXIV2API Xmpdatum : public Metadatum { |
33 | | public: |
34 | | //! @name Creators |
35 | | //@{ |
36 | | /*! |
37 | | @brief Constructor for new tags created by an application. The |
38 | | %Xmpdatum is created from a key / value pair. %Xmpdatum |
39 | | copies (clones) the value if one is provided. Alternatively, a |
40 | | program can create an 'empty' %Xmpdatum with only a key and |
41 | | set the value using setValue(). |
42 | | |
43 | | @param key The key of the %Xmpdatum. |
44 | | @param pValue Pointer to a %Xmpdatum value. |
45 | | @throw Error if the key cannot be parsed and converted |
46 | | to a known schema namespace prefix and property name. |
47 | | */ |
48 | | explicit Xmpdatum(const XmpKey& key, const Value* pValue = nullptr); |
49 | | //! Copy constructor |
50 | | Xmpdatum(const Xmpdatum& rhs); |
51 | | //! Destructor |
52 | | ~Xmpdatum() override; |
53 | | //@} |
54 | | |
55 | | //! @name Manipulators |
56 | | //@{ |
57 | | //! Assignment operator |
58 | | Xmpdatum& operator=(const Xmpdatum& rhs); |
59 | | /*! |
60 | | @brief Assign std::string \em value to the %Xmpdatum. |
61 | | Calls setValue(const std::string&). |
62 | | */ |
63 | | template <typename T> |
64 | | Xmpdatum& operator=(const T& value); |
65 | | /*! |
66 | | @brief Assign Value \em value to the %Xmpdatum. |
67 | | Calls setValue(const Value*). |
68 | | */ |
69 | | void setValue(const Value* pValue) override; |
70 | | /*! |
71 | | @brief Set the value to the string \em value. Uses Value::read(const |
72 | | std::string&). If the %Xmpdatum does not have a Value yet, |
73 | | then a %Value of the correct type for this %Xmpdatum is |
74 | | created. If the key is unknown, a XmpTextValue is used as |
75 | | default. Return 0 if the value was read successfully. |
76 | | */ |
77 | | int setValue(const std::string& value) override; |
78 | | //@} |
79 | | |
80 | | //! @name Accessors |
81 | | //@{ |
82 | | //! Not implemented. Calling this method will raise an exception. |
83 | | size_t copy(byte* buf, ByteOrder byteOrder) const override; |
84 | | std::ostream& write(std::ostream& os, const ExifData* pMetadata = nullptr) const override; |
85 | | /*! |
86 | | @brief Return the key of the Xmpdatum. The key is of the form |
87 | | '<b>Xmp</b>.prefix.property'. Note however that the |
88 | | key is not necessarily unique, i.e., an XmpData object may |
89 | | contain multiple metadata with the same key. |
90 | | */ |
91 | | [[nodiscard]] std::string key() const override; |
92 | | [[nodiscard]] const char* familyName() const override; |
93 | | //! Return the (preferred) schema namespace prefix. |
94 | | [[nodiscard]] std::string groupName() const override; |
95 | | //! Return the property name. |
96 | | [[nodiscard]] std::string tagName() const override; |
97 | | [[nodiscard]] std::string tagLabel() const override; |
98 | | [[nodiscard]] std::string tagDesc() const override; |
99 | | //! Properties don't have a tag number. Return 0. |
100 | | [[nodiscard]] uint16_t tag() const override; |
101 | | [[nodiscard]] TypeId typeId() const override; |
102 | | [[nodiscard]] const char* typeName() const override; |
103 | | // Todo: Remove this method from the baseclass |
104 | | //! The Exif typeSize doesn't make sense here. Return 0. |
105 | | [[nodiscard]] size_t typeSize() const override; |
106 | | [[nodiscard]] size_t count() const override; |
107 | | [[nodiscard]] size_t size() const override; |
108 | | [[nodiscard]] std::string toString() const override; |
109 | | [[nodiscard]] std::string toString(size_t n) const override; |
110 | | [[nodiscard]] int64_t toInt64(size_t n = 0) const override; |
111 | | [[nodiscard]] float toFloat(size_t n = 0) const override; |
112 | | [[nodiscard]] Rational toRational(size_t n = 0) const override; |
113 | | [[nodiscard]] std::unique_ptr<Value> getValue() const override; |
114 | | [[nodiscard]] const Value& value() const override; |
115 | | //@} |
116 | | |
117 | | private: |
118 | | // Pimpl idiom |
119 | | struct Impl; |
120 | | std::unique_ptr<Impl> p_; |
121 | | |
122 | | }; // class Xmpdatum |
123 | | |
124 | | //! Container type to hold all metadata |
125 | | using XmpMetadata = std::vector<Xmpdatum>; |
126 | | |
127 | | /*! |
128 | | @brief A container for XMP data. This is a top-level class of |
129 | | the %Exiv2 library. |
130 | | |
131 | | Provide high-level access to the XMP data of an image: |
132 | | - read XMP information from an XML block |
133 | | - access metadata through keys and standard C++ iterators |
134 | | - add, modify and delete metadata |
135 | | - serialize XMP data to an XML block |
136 | | */ |
137 | | class EXIV2API XmpData { |
138 | | public: |
139 | | //! Default constructor |
140 | 6.88k | XmpData() = default; |
141 | | |
142 | | //! XmpMetadata iterator type |
143 | | using iterator = XmpMetadata::iterator; |
144 | | //! XmpMetadata const iterator type |
145 | | using const_iterator = XmpMetadata::const_iterator; |
146 | | |
147 | | //! @name Manipulators |
148 | | //@{ |
149 | | /*! |
150 | | @brief Returns a reference to the %Xmpdatum that is associated with a |
151 | | particular \em key. If %XmpData does not already contain such |
152 | | an %Xmpdatum, operator[] adds object \em Xmpdatum(key). |
153 | | |
154 | | @note Since operator[] might insert a new element, it can't be a const |
155 | | member function. |
156 | | */ |
157 | | Xmpdatum& operator[](const std::string& key); |
158 | | /*! |
159 | | @brief Add an %Xmpdatum from the supplied key and value pair. This |
160 | | method copies (clones) the value. |
161 | | @return 0 if successful. |
162 | | */ |
163 | | int add(const XmpKey& key, const Value* value); |
164 | | /*! |
165 | | @brief Add a copy of the Xmpdatum to the XMP metadata. |
166 | | @return 0 if successful. |
167 | | */ |
168 | | int add(const Xmpdatum& xmpDatum); |
169 | | /* |
170 | | @brief Delete the Xmpdatum at iterator position pos, return the |
171 | | position of the next Xmpdatum. |
172 | | |
173 | | @note Iterators into the metadata, including pos, are potentially |
174 | | invalidated by this call. |
175 | | @brief Delete the Xmpdatum at iterator position pos and update pos |
176 | | */ |
177 | | iterator erase(XmpData::iterator pos); |
178 | | /*! |
179 | | @brief Delete the Xmpdatum at iterator position pos and update pos |
180 | | erases all following keys from the same family |
181 | | See: https://github.com/Exiv2/exiv2/issues/521 |
182 | | */ |
183 | | void eraseFamily(XmpData::iterator& pos); |
184 | | //! Delete all Xmpdatum instances resulting in an empty container. |
185 | | void clear(); |
186 | | //! Sort metadata by key |
187 | | void sortByKey(); |
188 | | //! Begin of the metadata |
189 | | iterator begin(); |
190 | | //! End of the metadata |
191 | | iterator end(); |
192 | | /*! |
193 | | @brief Find the first Xmpdatum with the given key, return an iterator |
194 | | to it. |
195 | | */ |
196 | | iterator findKey(const XmpKey& key); |
197 | | //@} |
198 | | |
199 | | //! @name Accessors |
200 | | //@{ |
201 | | //! Begin of the metadata |
202 | | [[nodiscard]] const_iterator begin() const; |
203 | | //! End of the metadata |
204 | | [[nodiscard]] const_iterator end() const; |
205 | | /*! |
206 | | @brief Find the first Xmpdatum with the given key, return a const |
207 | | iterator to it. |
208 | | */ |
209 | | [[nodiscard]] const_iterator findKey(const XmpKey& key) const; |
210 | | //! Return true if there is no XMP metadata |
211 | | [[nodiscard]] bool empty() const; |
212 | | //! Get the number of metadata entries |
213 | | [[nodiscard]] long count() const; |
214 | | |
215 | | //! are we to use the packet? |
216 | 0 | [[nodiscard]] bool usePacket() const { |
217 | 0 | return usePacket_; |
218 | 0 | } |
219 | | |
220 | | //! set usePacket_ |
221 | 8.67k | bool usePacket(bool b) { |
222 | 8.67k | bool r = usePacket_; |
223 | 8.67k | usePacket_ = b; |
224 | 8.67k | return r; |
225 | 8.67k | } |
226 | | //! setPacket |
227 | 8.67k | void setPacket(std::string xmpPacket) { |
228 | 8.67k | xmpPacket_ = std::move(xmpPacket); |
229 | 8.67k | usePacket(false); |
230 | 8.67k | } |
231 | | // ! getPacket |
232 | 0 | [[nodiscard]] const std::string& xmpPacket() const { |
233 | 0 | return xmpPacket_; |
234 | 0 | } |
235 | | |
236 | | //@} |
237 | | |
238 | | private: |
239 | | // DATA |
240 | | XmpMetadata xmpMetadata_; |
241 | | std::string xmpPacket_; |
242 | | bool usePacket_{}; |
243 | | //! Per-instance prefix-to-URI namespace bindings, see nsBindings(). |
244 | | std::map<std::string, std::string> nsBindings_; |
245 | | |
246 | | /*! |
247 | | @brief Namespace prefix-to-URI bindings captured from this object's own |
248 | | source packet at decode time. |
249 | | |
250 | | XMP namespace prefixes are document-scoped: two images may legitimately |
251 | | bind the same prefix to different URIs. Exiv2 keys are prefix-based and |
252 | | the URI is otherwise re-derived from a process-global registry at encode |
253 | | time, which lets one image's binding (including a corrupted one) leak into |
254 | | another. These per-instance bindings let encode() resolve each property's |
255 | | URI from the data it actually came from, falling back to the global |
256 | | registry only for keys that were added without a source packet. |
257 | | |
258 | | @note Internal: read by XmpParser::resolveNamespace() during encode(). |
259 | | */ |
260 | 0 | [[nodiscard]] const std::map<std::string, std::string>& nsBindings() const { |
261 | 0 | return nsBindings_; |
262 | 0 | } |
263 | | |
264 | | int addUnlocked(const XmpKey& key, const Value* value, const XmpProperties::XmpLock&); |
265 | | int addUnlocked(const Xmpdatum& xmpDatum, const XmpProperties::XmpLock&); |
266 | | bool emptyUnlocked(const XmpProperties::XmpLock&) const; |
267 | | long countUnlocked(const XmpProperties::XmpLock&) const; |
268 | | void sortByKeyUnlocked(const XmpProperties::XmpLock&); |
269 | | void clearUnlocked(const XmpProperties::XmpLock&); |
270 | | friend class XmpParser; |
271 | | }; // class XmpData |
272 | | |
273 | | /*! |
274 | | @brief Stateless parser class for XMP packets. Images use this |
275 | | class to parse and serialize XMP packets. The parser uses |
276 | | the XMP toolkit to do the job. |
277 | | */ |
278 | | class EXIV2API XmpParser { |
279 | | public: |
280 | | //! Options to control the format of the serialized XMP packet. |
281 | | enum XmpFormatFlags { |
282 | | omitPacketWrapper = 0x0010UL, //!< Omit the XML packet wrapper. |
283 | | readOnlyPacket = 0x0020UL, //!< Default is a writeable packet. |
284 | | useCompactFormat = 0x0040UL, //!< Use a compact form of RDF. |
285 | | includeThumbnailPad = 0x0100UL, //!< Include a padding allowance for a thumbnail image. |
286 | | exactPacketLength = 0x0200UL, //!< The padding parameter is the overall packet length. |
287 | | writeAliasComments = 0x0400UL, //!< Show aliases as XML comments. |
288 | | omitAllFormatting = 0x0800UL //!< Omit all formatting whitespace. |
289 | | }; |
290 | | /*! |
291 | | @brief Decode XMP metadata from an XMP packet \em xmpPacket into |
292 | | \em xmpData. The format of the XMP packet must follow the |
293 | | XMP specification. This method clears any previous contents |
294 | | of \em xmpData. |
295 | | |
296 | | @param xmpData Container for the decoded XMP properties |
297 | | @param xmpPacket The raw XMP packet to decode |
298 | | @return 0 if successful;<BR> |
299 | | 1 if XMP support has not been compiled-in;<BR> |
300 | | 2 if the XMP toolkit failed to initialize;<BR> |
301 | | 3 if the XMP toolkit failed and raised an XMP_Error |
302 | | */ |
303 | | static int decode(XmpData& xmpData, const std::string& xmpPacket); |
304 | | /*! |
305 | | @brief Encode (serialize) XMP metadata from \em xmpData into a |
306 | | string xmpPacket. The XMP packet returned in the string |
307 | | follows the XMP specification. This method only modifies |
308 | | \em xmpPacket if the operations succeeds (return code 0). |
309 | | |
310 | | @param xmpPacket Reference to a string to hold the encoded XMP |
311 | | packet. |
312 | | @param xmpData XMP properties to encode. |
313 | | @param formatFlags Flags that control the format of the XMP packet, |
314 | | see enum XmpFormatFlags. |
315 | | @param padding Padding length. |
316 | | @return 0 if successful;<BR> |
317 | | 1 if XMP support has not been compiled-in;<BR> |
318 | | 2 if the XMP toolkit failed to initialize;<BR> |
319 | | 3 if the XMP toolkit failed and raised an XMP_Error |
320 | | */ |
321 | | static int encode(std::string& xmpPacket, const XmpData& xmpData, uint16_t formatFlags = useCompactFormat, |
322 | | uint32_t padding = 0); |
323 | | |
324 | | /*! |
325 | | @deprecated This function is no longer needed and does absolutely nothing. |
326 | | XMP Toolkit initialization is handled automatically. |
327 | | Arguments are ignored. |
328 | | |
329 | | @return Always returns true. |
330 | | */ |
331 | | [[deprecated( |
332 | | "XmpParser::initialize is deprecated and does nothing. The XMP Toolkit is initialized " |
333 | | "automatically.")]] static bool |
334 | | initialize(void (*)(void*, bool) = nullptr, void* = nullptr); |
335 | | |
336 | | /*! |
337 | | @deprecated This function is no longer needed and does absolutely nothing. |
338 | | XMP Toolkit termination is handled automatically. |
339 | | */ |
340 | | [[deprecated( |
341 | | "XmpParser::terminate is deprecated and does nothing. The XMP Toolkit termination is handled " |
342 | | "automatically.")]] static void |
343 | | terminate(); |
344 | | /*! |
345 | | @brief Clear all custom namespaces registered with the XMP Toolkit. |
346 | | This is useful for resetting the registry state in tests. |
347 | | */ |
348 | | static void clearCustomNamespaces(); |
349 | | |
350 | | private: |
351 | | /*! |
352 | | @brief Register a namespace with the XMP Toolkit. |
353 | | */ |
354 | | static void registerNs(const std::string& ns, const std::string& prefix); |
355 | | /*! |
356 | | @brief Delete a namespace from the XMP Toolkit. |
357 | | |
358 | | XmpProperties::unregisterNs calls this to synchronize namespaces. |
359 | | */ |
360 | | static void unregisterNs(const std::string& ns); |
361 | | |
362 | | /*! |
363 | | @brief Register a namespace with the XMP Toolkit without locking. |
364 | | Assumes the lock obtained via XmpProperties::XmpLock is already held by caller. |
365 | | */ |
366 | | static void registerNsImpl(const std::string& ns, const std::string& prefix); |
367 | | |
368 | | static void registeredNamespacesUnlocked(Exiv2::Dictionary&, const XmpProperties::XmpLock&); |
369 | | |
370 | | /*! |
371 | | @brief Get namespaces registered with XMPsdk |
372 | | */ |
373 | | static void registeredNamespaces(Exiv2::Dictionary&); |
374 | | |
375 | | friend class XmpProperties; // permit XmpProperties -> registerNs() and registeredNamespaces() |
376 | | |
377 | | static std::unique_ptr<XmpKey> makeXmpKey(const std::string& schemaNs, const std::string& propPath, |
378 | | const XmpProperties::XmpLock&); |
379 | | |
380 | | /*! |
381 | | @brief Resolve a namespace prefix to its URI for serialization, preferring the |
382 | | binding declared by \em xmpData's own source packet and falling back to |
383 | | the process-global registry for keys added without one. |
384 | | Assumes the lock obtained via XmpProperties::XmpLock is held by caller. |
385 | | */ |
386 | | static std::string resolveNamespace(const XmpData& xmpData, const std::string& prefix, const XmpProperties::XmpLock&); |
387 | | |
388 | | }; // class XmpParser |
389 | | |
390 | | // ***************************************************************************** |
391 | | // free functions, template and inline definitions |
392 | | |
393 | | #if __cpp_if_constexpr |
394 | | template <typename T> |
395 | 314 | Xmpdatum& Xmpdatum::operator=(const T& value) { |
396 | | if constexpr (std::is_same_v<T, bool>) |
397 | 0 | setValue(value ? "True" : "False"); |
398 | | else if constexpr (std::is_convertible_v<T, std::string>) |
399 | 156 | setValue(value); |
400 | | else if constexpr (std::is_base_of_v<Value, T>) |
401 | 0 | setValue(&value); |
402 | | else |
403 | 158 | setValue(Exiv2::toString(value)); |
404 | 314 | return *this; |
405 | 314 | } Unexecuted instantiation: Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<Exiv2::Value>(Exiv2::Value const&) Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Line | Count | Source | 395 | 124 | Xmpdatum& Xmpdatum::operator=(const T& value) { | 396 | | if constexpr (std::is_same_v<T, bool>) | 397 | | setValue(value ? "True" : "False"); | 398 | | else if constexpr (std::is_convertible_v<T, std::string>) | 399 | 124 | setValue(value); | 400 | | else if constexpr (std::is_base_of_v<Value, T>) | 401 | | setValue(&value); | 402 | | else | 403 | | setValue(Exiv2::toString(value)); | 404 | 124 | return *this; | 405 | 124 | } |
Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<double>(double const&) Line | Count | Source | 395 | 56 | Xmpdatum& Xmpdatum::operator=(const T& value) { | 396 | | if constexpr (std::is_same_v<T, bool>) | 397 | | setValue(value ? "True" : "False"); | 398 | | else if constexpr (std::is_convertible_v<T, std::string>) | 399 | | setValue(value); | 400 | | else if constexpr (std::is_base_of_v<Value, T>) | 401 | | setValue(&value); | 402 | | else | 403 | 56 | setValue(Exiv2::toString(value)); | 404 | 56 | return *this; | 405 | 56 | } |
Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<unsigned long>(unsigned long const&) Line | Count | Source | 395 | 26 | Xmpdatum& Xmpdatum::operator=(const T& value) { | 396 | | if constexpr (std::is_same_v<T, bool>) | 397 | | setValue(value ? "True" : "False"); | 398 | | else if constexpr (std::is_convertible_v<T, std::string>) | 399 | | setValue(value); | 400 | | else if constexpr (std::is_base_of_v<Value, T>) | 401 | | setValue(&value); | 402 | | else | 403 | 26 | setValue(Exiv2::toString(value)); | 404 | 26 | return *this; | 405 | 26 | } |
Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<unsigned int>(unsigned int const&) Line | Count | Source | 395 | 74 | Xmpdatum& Xmpdatum::operator=(const T& value) { | 396 | | if constexpr (std::is_same_v<T, bool>) | 397 | | setValue(value ? "True" : "False"); | 398 | | else if constexpr (std::is_convertible_v<T, std::string>) | 399 | | setValue(value); | 400 | | else if constexpr (std::is_base_of_v<Value, T>) | 401 | | setValue(&value); | 402 | | else | 403 | 74 | setValue(Exiv2::toString(value)); | 404 | 74 | return *this; | 405 | 74 | } |
Unexecuted instantiation: Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<unsigned char const*>(unsigned char const* const&) Unexecuted instantiation: Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<char [4]>(char const (&) [4]) Unexecuted instantiation: Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<long>(long const&) Unexecuted instantiation: Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<float>(float const&) Unexecuted instantiation: Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<char [18]>(char const (&) [18]) Unexecuted instantiation: Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<unsigned short>(unsigned short const&) Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<short>(short const&) Line | Count | Source | 395 | 2 | Xmpdatum& Xmpdatum::operator=(const T& value) { | 396 | | if constexpr (std::is_same_v<T, bool>) | 397 | | setValue(value ? "True" : "False"); | 398 | | else if constexpr (std::is_convertible_v<T, std::string>) | 399 | | setValue(value); | 400 | | else if constexpr (std::is_base_of_v<Value, T>) | 401 | | setValue(&value); | 402 | | else | 403 | 2 | setValue(Exiv2::toString(value)); | 404 | 2 | return *this; | 405 | 2 | } |
Unexecuted instantiation: Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<unsigned char*>(unsigned char* const&) Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<char const*>(char const* const&) Line | Count | Source | 395 | 32 | Xmpdatum& Xmpdatum::operator=(const T& value) { | 396 | | if constexpr (std::is_same_v<T, bool>) | 397 | | setValue(value ? "True" : "False"); | 398 | | else if constexpr (std::is_convertible_v<T, std::string>) | 399 | 32 | setValue(value); | 400 | | else if constexpr (std::is_base_of_v<Value, T>) | 401 | | setValue(&value); | 402 | | else | 403 | | setValue(Exiv2::toString(value)); | 404 | 32 | return *this; | 405 | 32 | } |
Unexecuted instantiation: Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<int>(int const&) Unexecuted instantiation: Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<char [12]>(char const (&) [12]) Unexecuted instantiation: Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<bool>(bool const&) |
406 | | #else |
407 | | template <typename T> |
408 | | std::enable_if_t<std::is_convertible<T, std::string>::value> operatorHelper(Xmpdatum* xmp, const T& value) { |
409 | | xmp->setValue(value); |
410 | | } |
411 | | |
412 | | template <typename T> |
413 | | std::enable_if_t<std::is_base_of<Value, T>::value> operatorHelper(Xmpdatum* xmp, const T& value) { |
414 | | xmp->setValue(&value); |
415 | | } |
416 | | |
417 | | template <typename T> |
418 | | std::enable_if_t<std::is_same<T, bool>::value> operatorHelper(Xmpdatum* xmp, const T& value) { |
419 | | xmp->setValue(value ? "True" : "False"); |
420 | | } |
421 | | |
422 | | template <typename T> |
423 | | std::enable_if_t<!(std::is_convertible<T, std::string>::value || std::is_base_of<Value, T>::value || |
424 | | std::is_same<T, bool>::value)> |
425 | | operatorHelper(Xmpdatum* xmp, const T& value) { |
426 | | xmp->setValue(Exiv2::toString(value)); |
427 | | } |
428 | | |
429 | | template <typename T> |
430 | | Xmpdatum& Xmpdatum::operator=(const T& value) { |
431 | | operatorHelper(this, value); |
432 | | return *this; |
433 | | } |
434 | | #endif |
435 | | } // namespace Exiv2 |
436 | | |
437 | | #endif // EXIV2_XMP_EXIV2_HPP |