Project

General

Profile

Feature #937 » xmp.cpp

DwC patched - Alan Pater, 21 Dec 2013 15:52

 
1
// ***************************************************************** -*- C++ -*-
2
/*
3
 * Copyright (C) 2004-2013 Andreas Huggel <ahuggel@gmx.net>
4
 *
5
 * This program is part of the Exiv2 distribution.
6
 *
7
 * This program is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU General Public License
9
 * as published by the Free Software Foundation; either version 2
10
 * of the License, or (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
20
 */
21
/*
22
  File:      xmp.cpp
23
  Version:   $Rev: 3201 $
24
  Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net>
25
  History:   13-July-07, ahu: created
26
 */
27
// *****************************************************************************
28
#include "rcsid_int.hpp"
29
EXIV2_RCSID("@(#) $Id: xmp.cpp 3201 2013-12-01 12:13:42Z ahuggel $")
30

    
31
// *****************************************************************************
32
// included header files
33
#include "xmp.hpp"
34
#include "types.hpp"
35
#include "error.hpp"
36
#include "value.hpp"
37
#include "properties.hpp"
38

    
39
// + standard includes
40
#include <iostream>
41
#include <algorithm>
42
#include <cassert>
43
#include <string>
44

    
45
// Adobe XMP Toolkit
46
#ifdef EXV_HAVE_XMP_TOOLKIT
47
# define TXMP_STRING_TYPE std::string
48
# include <XMPSDK.hpp>
49
# include <XMP.incl_cpp>
50
#endif // EXV_HAVE_XMP_TOOLKIT
51

    
52
// *****************************************************************************
53
// local declarations
54
namespace {
55
    //! Unary predicate that matches an Xmpdatum by key
56
    class FindXmpdatum {
57
    public:
58
        //! Constructor, initializes the object with key
59
        FindXmpdatum(const Exiv2::XmpKey& key)
60
            : key_(key.key()) {}
61
        /*!
62
          @brief Returns true if prefix and property of the argument
63
                 Xmpdatum are equal to that of the object.
64
        */
65
        bool operator()(const Exiv2::Xmpdatum& xmpdatum) const
66
            { return key_ == xmpdatum.key(); }
67

    
68
    private:
69
        std::string key_;
70

    
71
    }; // class FindXmpdatum
72

    
73
#ifdef EXV_HAVE_XMP_TOOLKIT
74
    //! Convert XMP Toolkit struct option bit to Value::XmpStruct
75
    Exiv2::XmpValue::XmpStruct xmpStruct(const XMP_OptionBits& opt);
76

    
77
    //! Convert Value::XmpStruct to XMP Toolkit array option bits
78
    XMP_OptionBits xmpArrayOptionBits(Exiv2::XmpValue::XmpStruct xs);
79

    
80
    //! Convert XMP Toolkit array option bits to array TypeId
81
    Exiv2::TypeId arrayValueTypeId(const XMP_OptionBits& opt);
82

    
83
    //! Convert XMP Toolkit array option bits to Value::XmpArrayType
84
    Exiv2::XmpValue::XmpArrayType xmpArrayType(const XMP_OptionBits& opt);
85

    
86
    //! Convert Value::XmpArrayType to XMP Toolkit array option bits
87
    XMP_OptionBits xmpArrayOptionBits(Exiv2::XmpValue::XmpArrayType xat);
88

    
89
    //! Convert XmpFormatFlags to XMP Toolkit format option bits
90
    XMP_OptionBits xmpFormatOptionBits(Exiv2::XmpParser::XmpFormatFlags flags);
91

    
92
# ifdef DEBUG
93
    //! Print information about a parsed XMP node
94
    void printNode(const std::string& schemaNs,
95
                   const std::string& propPath,
96
                   const std::string& propValue,
97
                   const XMP_OptionBits& opt);
98
# endif // DEBUG
99
#endif // EXV_HAVE_XMP_TOOLKIT
100

    
101
    //! Make an XMP key from a schema namespace and property path
102
    Exiv2::XmpKey::AutoPtr makeXmpKey(const std::string& schemaNs,
103
                                      const std::string& propPath);
104

    
105
    //! Helper class used to serialize critical sections
106
    class AutoLock
107
    {
108
    public:
109
        AutoLock(Exiv2::XmpParser::XmpLockFct xmpLockFct, void* pLockData)
110
            : xmpLockFct_(xmpLockFct), pLockData_(pLockData)
111
        {
112
            if (xmpLockFct_) xmpLockFct_(pLockData_, true);
113
        }
114
        ~AutoLock()
115
        {
116
            if (xmpLockFct_) xmpLockFct_(pLockData_, false);
117
        }
118
    private:
119
        Exiv2::XmpParser::XmpLockFct xmpLockFct_;
120
        void* pLockData_;
121
    };
122
}
123

    
124
// *****************************************************************************
125
// class member definitions
126
namespace Exiv2 {
127

    
128
    //! Internal Pimpl structure of class Xmpdatum.
129
    struct Xmpdatum::Impl {
130
        Impl(const XmpKey& key, const Value* pValue);  //!< Constructor
131
        Impl(const Impl& rhs);                         //!< Copy constructor
132
        Impl& operator=(const Impl& rhs);              //!< Assignment
133

    
134
        // DATA
135
        XmpKey::AutoPtr key_;                          //!< Key
136
        Value::AutoPtr  value_;                        //!< Value
137
    };
138

    
139
    Xmpdatum::Impl::Impl(const XmpKey& key, const Value* pValue)
140
        : key_(key.clone())
141
    {
142
        if (pValue) value_ = pValue->clone();
143
    }
144

    
145
    Xmpdatum::Impl::Impl(const Impl& rhs)
146
    {
147
        if (rhs.key_.get() != 0) key_ = rhs.key_->clone(); // deep copy
148
        if (rhs.value_.get() != 0) value_ = rhs.value_->clone(); // deep copy
149
    }
150

    
151
    Xmpdatum::Impl& Xmpdatum::Impl::operator=(const Impl& rhs)
152
    {
153
        if (this == &rhs) return *this;
154
        key_.reset();
155
        if (rhs.key_.get() != 0) key_ = rhs.key_->clone(); // deep copy
156
        value_.reset();
157
        if (rhs.value_.get() != 0) value_ = rhs.value_->clone(); // deep copy
158
        return *this;
159
    }
160

    
161
    Xmpdatum::Xmpdatum(const XmpKey& key, const Value* pValue)
162
        : p_(new Impl(key, pValue))
163
    {
164
    }
165

    
166
    Xmpdatum::Xmpdatum(const Xmpdatum& rhs)
167
        : Metadatum(rhs), p_(new Impl(*rhs.p_))
168
    {
169
    }
170

    
171
    Xmpdatum& Xmpdatum::operator=(const Xmpdatum& rhs)
172
    {
173
        if (this == &rhs) return *this;
174
        Metadatum::operator=(rhs);
175
        *p_ = *rhs.p_;
176
        return *this;
177
    }
178

    
179
    Xmpdatum::~Xmpdatum()
180
    {
181
        delete p_;
182
    }
183

    
184
    std::string Xmpdatum::key() const
185
    {
186
        return p_->key_.get() == 0 ? "" : p_->key_->key();
187
    }
188

    
189
    const char* Xmpdatum::familyName() const
190
    {
191
        return p_->key_.get() == 0 ? "" : p_->key_->familyName();
192
    }
193

    
194
    std::string Xmpdatum::groupName() const
195
    {
196
        return p_->key_.get() == 0 ? "" : p_->key_->groupName();
197
    }
198

    
199
    std::string Xmpdatum::tagName() const
200
    {
201
        return p_->key_.get() == 0 ? "" : p_->key_->tagName();
202
    }
203

    
204
    std::string Xmpdatum::tagLabel() const
205
    {
206
        return p_->key_.get() == 0 ? "" : p_->key_->tagLabel();
207
    }
208

    
209
    uint16_t Xmpdatum::tag() const
210
    {
211
        return p_->key_.get() == 0 ? 0 : p_->key_->tag();
212
    }
213

    
214
    TypeId Xmpdatum::typeId() const
215
    {
216
        return p_->value_.get() == 0 ? invalidTypeId : p_->value_->typeId();
217
    }
218

    
219
    const char* Xmpdatum::typeName() const
220
    {
221
        return TypeInfo::typeName(typeId());
222
    }
223

    
224
    long Xmpdatum::typeSize() const
225
    {
226
        return 0;
227
    }
228

    
229
    long Xmpdatum::count() const
230
    {
231
        return p_->value_.get() == 0 ? 0 : p_->value_->count();
232
    }
233

    
234
    long Xmpdatum::size() const
235
    {
236
        return p_->value_.get() == 0 ? 0 : p_->value_->size();
237
    }
238

    
239
    std::string Xmpdatum::toString() const
240
    {
241
        return p_->value_.get() == 0 ? "" : p_->value_->toString();
242
    }
243

    
244
    std::string Xmpdatum::toString(long n) const
245
    {
246
        return p_->value_.get() == 0 ? "" : p_->value_->toString(n);
247
    }
248

    
249
    long Xmpdatum::toLong(long n) const
250
    {
251
        return p_->value_.get() == 0 ? -1 : p_->value_->toLong(n);
252
    }
253

    
254
    float Xmpdatum::toFloat(long n) const
255
    {
256
        return p_->value_.get() == 0 ? -1 : p_->value_->toFloat(n);
257
    }
258

    
259
    Rational Xmpdatum::toRational(long n) const
260
    {
261
        return p_->value_.get() == 0 ? Rational(-1, 1) : p_->value_->toRational(n);
262
    }
263

    
264
    Value::AutoPtr Xmpdatum::getValue() const
265
    {
266
        return p_->value_.get() == 0 ? Value::AutoPtr(0) : p_->value_->clone();
267
    }
268

    
269
    const Value& Xmpdatum::value() const
270
    {
271
        if (p_->value_.get() == 0) throw Error(8);
272
        return *p_->value_;
273
    }
274

    
275
    long Xmpdatum::copy(byte* /*buf*/, ByteOrder /*byteOrder*/) const
276
    {
277
        throw Error(34, "Xmpdatum::copy");
278
        return 0;
279
    }
280

    
281
    std::ostream& Xmpdatum::write(std::ostream& os, const ExifData*) const
282
    {
283
        return XmpProperties::printProperty(os, key(), value());
284
    }
285

    
286
    Xmpdatum& Xmpdatum::operator=(const std::string& value)
287
    {
288
        setValue(value);
289
        return *this;
290
    }
291

    
292
    Xmpdatum& Xmpdatum::operator=(const Value& value)
293
    {
294
        setValue(&value);
295
        return *this;
296
    }
297

    
298
    void Xmpdatum::setValue(const Value* pValue)
299
    {
300
        p_->value_.reset();
301
        if (pValue) p_->value_ = pValue->clone();
302
    }
303

    
304
    int Xmpdatum::setValue(const std::string& value)
305
    {
306
        if (p_->value_.get() == 0) {
307
            TypeId type = xmpText;
308
            if (0 != p_->key_.get()) {
309
                type = XmpProperties::propertyType(*p_->key_.get());
310
            }
311
            p_->value_ = Value::create(type);
312
        }
313
        return p_->value_->read(value);
314
    }
315

    
316
    Xmpdatum& XmpData::operator[](const std::string& key)
317
    {
318
        XmpKey xmpKey(key);
319
        iterator pos = findKey(xmpKey);
320
        if (pos == end()) {
321
            add(Xmpdatum(xmpKey));
322
            pos = findKey(xmpKey);
323
        }
324
        return *pos;
325
    }
326

    
327
    int XmpData::add(const XmpKey& key, const Value* value)
328
    {
329
        return add(Xmpdatum(key, value));
330
    }
331

    
332
    int XmpData::add(const Xmpdatum& xmpDatum)
333
    {
334
        xmpMetadata_.push_back(xmpDatum);
335
        return 0;
336
    }
337

    
338
    XmpData::const_iterator XmpData::findKey(const XmpKey& key) const
339
    {
340
        return std::find_if(xmpMetadata_.begin(), xmpMetadata_.end(),
341
                            FindXmpdatum(key));
342
    }
343

    
344
    XmpData::iterator XmpData::findKey(const XmpKey& key)
345
    {
346
        return std::find_if(xmpMetadata_.begin(), xmpMetadata_.end(),
347
                            FindXmpdatum(key));
348
    }
349

    
350
    void XmpData::clear()
351
    {
352
        xmpMetadata_.clear();
353
    }
354

    
355
    void XmpData::sortByKey()
356
    {
357
        std::sort(xmpMetadata_.begin(), xmpMetadata_.end(), cmpMetadataByKey);
358
    }
359

    
360
    XmpData::const_iterator XmpData::begin() const
361
    {
362
        return xmpMetadata_.begin();
363
    }
364

    
365
    XmpData::const_iterator XmpData::end() const
366
    {
367
        return xmpMetadata_.end();
368
    }
369

    
370
    bool XmpData::empty() const
371
    {
372
        return count() == 0;
373
    }
374

    
375
    long XmpData::count() const
376
    {
377
        return static_cast<long>(xmpMetadata_.size());
378
    }
379

    
380
    XmpData::iterator XmpData::begin()
381
    {
382
        return xmpMetadata_.begin();
383
    }
384

    
385
    XmpData::iterator XmpData::end()
386
    {
387
        return xmpMetadata_.end();
388
    }
389

    
390
    XmpData::iterator XmpData::erase(XmpData::iterator pos)
391
    {
392
        return xmpMetadata_.erase(pos);
393
    }
394

    
395
    bool XmpParser::initialized_ = false;
396
    XmpParser::XmpLockFct XmpParser::xmpLockFct_ = 0;
397
    void* XmpParser::pLockData_ = 0;
398

    
399
    bool XmpParser::initialize(XmpParser::XmpLockFct xmpLockFct, void* pLockData)
400
    {
401
        if (!initialized_) {
402
#ifdef EXV_HAVE_XMP_TOOLKIT
403
            xmpLockFct_ = xmpLockFct;
404
            pLockData_ = pLockData;
405
            initialized_ = SXMPMeta::Initialize();
406
	    SXMPMeta::RegisterNamespace("http://rs.tdwg.org/dwc/terms/", "dwc");
407
            SXMPMeta::RegisterNamespace("http://www.digikam.org/ns/1.0/", "digiKam");
408
            SXMPMeta::RegisterNamespace("http://www.digikam.org/ns/kipi/1.0/", "kipi");
409
            SXMPMeta::RegisterNamespace("http://ns.microsoft.com/photo/1.0/", "MicrosoftPhoto");
410
            SXMPMeta::RegisterNamespace("http://iptc.org/std/Iptc4xmpExt/2008-02-29/", "iptcExt");
411
            SXMPMeta::RegisterNamespace("http://ns.useplus.org/ldf/xmp/1.0/", "plus");
412
            SXMPMeta::RegisterNamespace("http://ns.iview-multimedia.com/mediapro/1.0/", "mediapro");
413
            SXMPMeta::RegisterNamespace("http://ns.microsoft.com/expressionmedia/1.0/", "expressionmedia");
414
            SXMPMeta::RegisterNamespace("http://ns.microsoft.com/photo/1.2/", "MP");
415
            SXMPMeta::RegisterNamespace("http://ns.microsoft.com/photo/1.2/t/RegionInfo#", "MPRI");
416
            SXMPMeta::RegisterNamespace("http://ns.microsoft.com/photo/1.2/t/Region#", "MPReg");
417
            SXMPMeta::RegisterNamespace("http://www.metadataworkinggroup.com/schemas/regions/", "mwg-rs");
418
            SXMPMeta::RegisterNamespace("http://ns.adobe.com/xmp/sType/Area#", "stArea");
419
#else
420
            initialized_ = true;
421
#endif
422
        }
423
        return initialized_;
424
    }
425

    
426
    void XmpParser::terminate()
427
    {
428
        XmpProperties::unregisterNs();
429
        if (initialized_) {
430
#ifdef EXV_HAVE_XMP_TOOLKIT
431
            SXMPMeta::Terminate();
432
#endif
433
            xmpLockFct_ = 0;
434
            pLockData_ = 0;
435
            initialized_ = false;
436
        }
437
    }
438

    
439
#ifdef EXV_HAVE_XMP_TOOLKIT
440
    void XmpParser::registerNs(const std::string& ns,
441
                               const std::string& prefix)
442
    {
443
        try {
444
            initialize();
445
            AutoLock autoLock(xmpLockFct_, pLockData_);
446
            SXMPMeta::DeleteNamespace(ns.c_str());
447
            SXMPMeta::RegisterNamespace(ns.c_str(), prefix.c_str());
448
        }
449
        catch (const XMP_Error& e) {
450
            throw Error(40, e.GetID(), e.GetErrMsg());
451
        }
452
    } // XmpParser::registerNs
453
#else
454
    void XmpParser::registerNs(const std::string& /*ns*/,
455
                               const std::string& /*prefix*/)
456
    {
457
        initialize();
458
    } // XmpParser::registerNs
459
#endif
460

    
461
    void XmpParser::unregisterNs(const std::string& /*ns*/)
462
    {
463
#ifdef EXV_HAVE_XMP_TOOLKIT
464
        try {
465
// Throws XMP Toolkit error 8: Unimplemented method XMPMeta::DeleteNamespace
466
//          SXMPMeta::DeleteNamespace(ns.c_str());
467
        }
468
        catch (const XMP_Error& e) {
469
            throw Error(40, e.GetID(), e.GetErrMsg());
470
        }
471
#endif
472
    } // XmpParser::unregisterNs
473

    
474
#ifdef EXV_HAVE_XMP_TOOLKIT
475
    int XmpParser::decode(      XmpData&     xmpData,
476
                          const std::string& xmpPacket)
477
    { try {
478
        xmpData.clear();
479
        if (xmpPacket.empty()) return 0;
480

    
481
        if (!initialize()) {
482
#ifndef SUPPRESS_WARNINGS
483
            EXV_ERROR << "XMP toolkit initialization failed.\n";
484
#endif
485
            return 2;
486
        }
487

    
488
        SXMPMeta meta(xmpPacket.data(), static_cast<XMP_StringLen>(xmpPacket.size()));
489
        SXMPIterator iter(meta);
490
        std::string schemaNs, propPath, propValue;
491
        XMP_OptionBits opt;
492
        while (iter.Next(&schemaNs, &propPath, &propValue, &opt)) {
493
#ifdef DEBUG
494
            printNode(schemaNs, propPath, propValue, opt);
495
#endif
496
            if (XMP_PropIsAlias(opt)) {
497
                throw Error(47, schemaNs, propPath, propValue);
498
                continue;
499
            }
500
            if (XMP_NodeIsSchema(opt)) {
501
                // Register unknown namespaces with Exiv2
502
                // (Namespaces are automatically registered with the XMP Toolkit)
503
                if (XmpProperties::prefix(schemaNs).empty()) {
504
                    std::string prefix;
505
                    bool ret = meta.GetNamespacePrefix(schemaNs.c_str(), &prefix);
506
                    if (!ret) throw Error(45, schemaNs);
507
                    prefix = prefix.substr(0, prefix.size() - 1);
508
                    XmpProperties::registerNs(schemaNs, prefix);
509
                }
510
                continue;
511
            }
512
            XmpKey::AutoPtr key = makeXmpKey(schemaNs, propPath);
513
            if (XMP_ArrayIsAltText(opt)) {
514
                // Read Lang Alt property
515
                LangAltValue::AutoPtr val(new LangAltValue);
516
                XMP_Index count = meta.CountArrayItems(schemaNs.c_str(), propPath.c_str());
517
                while (count-- > 0) {
518
                    // Get the text
519
                    bool haveNext = iter.Next(&schemaNs, &propPath, &propValue, &opt);
520
#ifdef DEBUG
521
                    printNode(schemaNs, propPath, propValue, opt);
522
#endif
523
                    if (   !haveNext
524
                        || !XMP_PropIsSimple(opt)
525
                        || !XMP_PropHasLang(opt)) {
526
                        throw Error(41, propPath, opt);
527
                    }
528
                    const std::string text = propValue;
529
                    // Get the language qualifier
530
                    haveNext = iter.Next(&schemaNs, &propPath, &propValue, &opt);
531
#ifdef DEBUG
532
                    printNode(schemaNs, propPath, propValue, opt);
533
#endif
534
                    if (   !haveNext
535
                        || !XMP_PropIsSimple(opt)
536
                        || !XMP_PropIsQualifier(opt)
537
                        || propPath.substr(propPath.size() - 8, 8) != "xml:lang") {
538
                        throw Error(42, propPath, opt);
539
                    }
540
                    val->value_[propValue] = text;
541
                }
542
                xmpData.add(*key.get(), val.get());
543
                continue;
544
            }
545
            if (    XMP_PropIsArray(opt)
546
                && !XMP_PropHasQualifiers(opt)
547
                && !XMP_ArrayIsAltText(opt)) {
548
                // Check if all elements are simple
549
                bool simpleArray = true;
550
                SXMPIterator aIter(meta, schemaNs.c_str(), propPath.c_str());
551
                std::string aSchemaNs, aPropPath, aPropValue;
552
                XMP_OptionBits aOpt;
553
                while (aIter.Next(&aSchemaNs, &aPropPath, &aPropValue, &aOpt)) {
554
                    if (propPath == aPropPath) continue;
555
                    if (   !XMP_PropIsSimple(aOpt)
556
                        ||  XMP_PropHasQualifiers(aOpt)
557
                        ||  XMP_PropIsQualifier(aOpt)
558
                        ||  XMP_NodeIsSchema(aOpt)
559
                        ||  XMP_PropIsAlias(aOpt)) {
560
                        simpleArray = false;
561
                        break;
562
                    }
563
                }
564
                if (simpleArray) {
565
                    // Read the array into an XmpArrayValue
566
                    XmpArrayValue::AutoPtr val(new XmpArrayValue(arrayValueTypeId(opt)));
567
                    XMP_Index count = meta.CountArrayItems(schemaNs.c_str(), propPath.c_str());
568
                    while (count-- > 0) {
569
                        iter.Next(&schemaNs, &propPath, &propValue, &opt);
570
#ifdef DEBUG
571
                        printNode(schemaNs, propPath, propValue, opt);
572
#endif
573
                        val->read(propValue);
574
                    }
575
                    xmpData.add(*key.get(), val.get());
576
                    continue;
577
                }
578
            }
579
            XmpTextValue::AutoPtr val(new XmpTextValue);
580
            if (   XMP_PropIsStruct(opt)
581
                || XMP_PropIsArray(opt)) {
582
                // Create a metadatum with only XMP options
583
                val->setXmpArrayType(xmpArrayType(opt));
584
                val->setXmpStruct(xmpStruct(opt));
585
                xmpData.add(*key.get(), val.get());
586
                continue;
587
            }
588
            if (   XMP_PropIsSimple(opt)
589
                || XMP_PropIsQualifier(opt)) {
590
                val->read(propValue);
591
                xmpData.add(*key.get(), val.get());
592
                continue;
593
            }
594
            // Don't let any node go by unnoticed
595
            throw Error(39, key->key(), opt);
596
        } // iterate through all XMP nodes
597

    
598
        return 0;
599
    }
600
#ifndef SUPPRESS_WARNINGS
601
    catch (const XMP_Error& e) {
602
        EXV_ERROR << Error(40, e.GetID(), e.GetErrMsg()) << "\n";
603
        xmpData.clear();
604
        return 3;
605
    }
606
#else
607
    catch (const XMP_Error&) {
608
        xmpData.clear();
609
        return 3;
610
    }
611
#endif // SUPPRESS_WARNINGS
612
    } // XmpParser::decode
613
#else
614
    int XmpParser::decode(      XmpData&     xmpData,
615
                          const std::string& xmpPacket)
616
    {
617
        xmpData.clear();
618
        if (!xmpPacket.empty()) {
619
#ifndef SUPPRESS_WARNINGS
620
            EXV_WARNING << "XMP toolkit support not compiled in.\n";
621
#endif
622
        }
623
        return 1;
624
    } // XmpParser::decode
625
#endif // !EXV_HAVE_XMP_TOOLKIT
626

    
627
#ifdef EXV_HAVE_XMP_TOOLKIT
628
    int XmpParser::encode(      std::string& xmpPacket,
629
                          const XmpData&     xmpData,
630
                                uint16_t     formatFlags,
631
                                uint32_t     padding)
632
    { try {
633
        if (xmpData.empty()) {
634
            xmpPacket.clear();
635
            return 0;
636
        }
637

    
638
        if (!initialize()) {
639
#ifndef SUPPRESS_WARNINGS
640
            EXV_ERROR << "XMP toolkit initialization failed.\n";
641
#endif
642
            return 2;
643
        }
644
        // Register custom namespaces with XMP-SDK
645
        for (XmpProperties::NsRegistry::iterator i = XmpProperties::nsRegistry_.begin();
646
             i != XmpProperties::nsRegistry_.end(); ++i) {
647
#ifdef DEBUG
648
            std::cerr << "Registering " << i->second.prefix_ << " : " << i->first << "\n";
649
#endif
650
            registerNs(i->first, i->second.prefix_);
651
        }
652
        SXMPMeta meta;
653
        for (XmpData::const_iterator i = xmpData.begin(); i != xmpData.end(); ++i) {
654
            const std::string ns = XmpProperties::ns(i->groupName());
655
            XMP_OptionBits options = 0;
656

    
657
            if (i->typeId() == langAlt) {
658
                // Encode Lang Alt property
659
                const LangAltValue* la = dynamic_cast<const LangAltValue*>(&i->value());
660
                if (la == 0) throw Error(43, i->key());
661
                int idx = 1;
662
                // write the default first
663
                LangAltValue::ValueType::const_iterator k = la->value_.find("x-default");
664
                if (k != la->value_.end()) {
665
#ifdef DEBUG
666
                    printNode(ns, i->tagName(), k->second, 0);
667
#endif
668
                    meta.AppendArrayItem(ns.c_str(), i->tagName().c_str(), kXMP_PropArrayIsAlternate, k->second.c_str());
669
                    const std::string item = i->tagName() + "[" + toString(idx++) + "]";
670
                    meta.SetQualifier(ns.c_str(), item.c_str(), kXMP_NS_XML, "lang", k->first.c_str());
671
                }
672
                for (k = la->value_.begin(); k != la->value_.end(); ++k) {
673
                    if (k->first == "x-default") continue;
674
#ifdef DEBUG
675
                    printNode(ns, i->tagName(), k->second, 0);
676
#endif
677
                    meta.AppendArrayItem(ns.c_str(), i->tagName().c_str(), kXMP_PropArrayIsAlternate, k->second.c_str());
678
                    const std::string item = i->tagName() + "[" + toString(idx++) + "]";
679
                    meta.SetQualifier(ns.c_str(), item.c_str(), kXMP_NS_XML, "lang", k->first.c_str());
680
                }
681
                continue;
682
            }
683
            // Todo: Xmpdatum should have an XmpValue, not a Value
684
            const XmpValue* val = dynamic_cast<const XmpValue*>(&i->value());
685
            if (val == 0) throw Error(52, i->key(), i->typeName());
686
            options =   xmpArrayOptionBits(val->xmpArrayType())
687
                      | xmpArrayOptionBits(val->xmpStruct());
688
            if (   i->typeId() == xmpBag
689
                || i->typeId() == xmpSeq
690
                || i->typeId() == xmpAlt) {
691
#ifdef DEBUG
692
                printNode(ns, i->tagName(), "", options);
693
#endif
694
                meta.SetProperty(ns.c_str(), i->tagName().c_str(), 0, options);
695
                for (int idx = 0; idx < i->count(); ++idx) {
696
                    const std::string item = i->tagName() + "[" + toString(idx + 1) + "]";
697
#ifdef DEBUG
698
                    printNode(ns, item, i->toString(idx), 0);
699
#endif
700
                    meta.SetProperty(ns.c_str(), item.c_str(), i->toString(idx).c_str());
701
                }
702
                continue;
703
            }
704
            if (i->typeId() == xmpText) {
705
                if (i->count() == 0) {
706
#ifdef DEBUG
707
                    printNode(ns, i->tagName(), "", options);
708
#endif
709
                    meta.SetProperty(ns.c_str(), i->tagName().c_str(), 0, options);
710
                }
711
                else {
712
#ifdef DEBUG
713
                    printNode(ns, i->tagName(), i->toString(0), options);
714
#endif
715
                    meta.SetProperty(ns.c_str(), i->tagName().c_str(), i->toString(0).c_str(), options);
716
                }
717
                continue;
718
            }
719
            // Don't let any Xmpdatum go by unnoticed
720
            throw Error(38, i->tagName(), i->typeName());
721
        }
722
        std::string tmpPacket;
723
        meta.SerializeToBuffer(&tmpPacket, xmpFormatOptionBits(static_cast<XmpFormatFlags>(formatFlags)), padding); // throws
724
        xmpPacket = tmpPacket;
725

    
726
        return 0;
727
    }
728
#ifndef SUPPRESS_WARNINGS
729
    catch (const XMP_Error& e) {
730
        EXV_ERROR << Error(40, e.GetID(), e.GetErrMsg()) << "\n";
731
        return 3;
732
    }
733
#else
734
    catch (const XMP_Error&) {
735
        return 3;
736
    }
737
#endif // SUPPRESS_WARNINGS
738
    } // XmpParser::decode
739
#else
740
    int XmpParser::encode(      std::string& /*xmpPacket*/,
741
                          const XmpData&     xmpData,
742
                                uint16_t     /*formatFlags*/,
743
                                uint32_t     /*padding*/)
744
    {
745
        if (!xmpData.empty()) {
746
#ifndef SUPPRESS_WARNINGS
747
            EXV_WARNING << "XMP toolkit support not compiled in.\n";
748
#endif
749
        }
750
        return 1;
751
    } // XmpParser::encode
752
#endif // !EXV_HAVE_XMP_TOOLKIT
753

    
754
}                                       // namespace Exiv2
755

    
756
// *****************************************************************************
757
// local definitions
758
namespace {
759

    
760
#ifdef EXV_HAVE_XMP_TOOLKIT
761
    Exiv2::XmpValue::XmpStruct xmpStruct(const XMP_OptionBits& opt)
762
    {
763
        Exiv2::XmpValue::XmpStruct var(Exiv2::XmpValue::xsNone);
764
        if (XMP_PropIsStruct(opt)) {
765
            var = Exiv2::XmpValue::xsStruct;
766
        }
767
        return var;
768
    }
769

    
770
    XMP_OptionBits xmpArrayOptionBits(Exiv2::XmpValue::XmpStruct xs)
771
    {
772
        XMP_OptionBits var(0);
773
        switch (xs) {
774
        case Exiv2::XmpValue::xsNone:
775
            break;
776
        case Exiv2::XmpValue::xsStruct:
777
            XMP_SetOption(var, kXMP_PropValueIsStruct);
778
            break;
779
        }
780
        return var;
781
    }
782

    
783
    Exiv2::TypeId arrayValueTypeId(const XMP_OptionBits& opt)
784
    {
785
        Exiv2::TypeId typeId(Exiv2::invalidTypeId);
786
        if (XMP_PropIsArray(opt)) {
787
            if (XMP_ArrayIsAlternate(opt))      typeId = Exiv2::xmpAlt;
788
            else if (XMP_ArrayIsOrdered(opt))   typeId = Exiv2::xmpSeq;
789
            else if (XMP_ArrayIsUnordered(opt)) typeId = Exiv2::xmpBag;
790
        }
791
        return typeId;
792
    }
793

    
794
    Exiv2::XmpValue::XmpArrayType xmpArrayType(const XMP_OptionBits& opt)
795
    {
796
        return Exiv2::XmpValue::xmpArrayType(arrayValueTypeId(opt));
797
    }
798

    
799
    XMP_OptionBits xmpArrayOptionBits(Exiv2::XmpValue::XmpArrayType xat)
800
    {
801
        XMP_OptionBits var(0);
802
        switch (xat) {
803
        case Exiv2::XmpValue::xaNone:
804
            break;
805
        case Exiv2::XmpValue::xaAlt:
806
            XMP_SetOption(var, kXMP_PropValueIsArray);
807
            XMP_SetOption(var, kXMP_PropArrayIsAlternate);
808
            break;
809
        case Exiv2::XmpValue::xaSeq:
810
            XMP_SetOption(var, kXMP_PropValueIsArray);
811
            XMP_SetOption(var, kXMP_PropArrayIsOrdered);
812
            break;
813
        case Exiv2::XmpValue::xaBag:
814
            XMP_SetOption(var, kXMP_PropValueIsArray);
815
            break;
816
        }
817
        return var;
818
    }
819

    
820
    XMP_OptionBits xmpFormatOptionBits(Exiv2::XmpParser::XmpFormatFlags flags)
821
    {
822
        XMP_OptionBits var(0);
823
        if (flags & Exiv2::XmpParser::omitPacketWrapper)   var |= kXMP_OmitPacketWrapper;
824
        if (flags & Exiv2::XmpParser::readOnlyPacket)      var |= kXMP_ReadOnlyPacket;
825
        if (flags & Exiv2::XmpParser::useCompactFormat)    var |= kXMP_UseCompactFormat;
826
        if (flags & Exiv2::XmpParser::includeThumbnailPad) var |= kXMP_IncludeThumbnailPad;
827
        if (flags & Exiv2::XmpParser::exactPacketLength)   var |= kXMP_ExactPacketLength;
828
        if (flags & Exiv2::XmpParser::writeAliasComments)  var |= kXMP_WriteAliasComments;
829
        if (flags & Exiv2::XmpParser::omitAllFormatting)   var |= kXMP_OmitAllFormatting;
830
        return var;
831
    }
832

    
833
#ifdef DEBUG
834
    void printNode(const std::string& schemaNs,
835
                   const std::string& propPath,
836
                   const std::string& propValue,
837
                   const XMP_OptionBits& opt)
838
    {
839
        static bool first = true;
840
        if (first) {
841
            first = false;
842
            std::cout << "ashisabsals\n"
843
                      << "lcqqtrgqlai\n";
844
        }
845
        enum { alia=0, sche, hasq, isqu, stru, arra,
846
               abag, aseq, aalt, lang, simp, len };
847

    
848
        std::string opts(len, '.');
849
        if (XMP_PropIsAlias(opt))       opts[alia] = 'X';
850
        if (XMP_NodeIsSchema(opt))      opts[sche] = 'X';
851
        if (XMP_PropHasQualifiers(opt)) opts[hasq] = 'X';
852
        if (XMP_PropIsQualifier(opt))   opts[isqu] = 'X';
853
        if (XMP_PropIsStruct(opt))      opts[stru] = 'X';
854
        if (XMP_PropIsArray(opt))       opts[arra] = 'X';
855
        if (XMP_ArrayIsUnordered(opt))  opts[abag] = 'X';
856
        if (XMP_ArrayIsOrdered(opt))    opts[aseq] = 'X';
857
        if (XMP_ArrayIsAlternate(opt))  opts[aalt] = 'X';
858
        if (XMP_ArrayIsAltText(opt))    opts[lang] = 'X';
859
        if (XMP_PropIsSimple(opt))      opts[simp] = 'X';
860

    
861
        std::cout << opts << " ";
862
        if (opts[sche] == 'X') {
863
            std::cout << "ns=" << schemaNs;
864
        }
865
        else {
866
            std::cout << propPath << " = " << propValue;
867
        }
868
        std::cout << std::endl;
869
    }
870
#endif // DEBUG
871
#endif // EXV_HAVE_XMP_TOOLKIT
872

    
873
    Exiv2::XmpKey::AutoPtr makeXmpKey(const std::string& schemaNs,
874
                                      const std::string& propPath)
875
    {
876
        std::string property;
877
        std::string::size_type idx = propPath.find(':');
878
        if (idx == std::string::npos) {
879
            throw Exiv2::Error(44, propPath, schemaNs);
880
        }
881
        // Don't worry about out_of_range, XMP parser takes care of this
882
        property = propPath.substr(idx + 1);
883
        std::string prefix = Exiv2::XmpProperties::prefix(schemaNs);
884
        if (prefix.empty()) {
885
            throw Exiv2::Error(36, propPath, schemaNs);
886
        }
887
        return Exiv2::XmpKey::AutoPtr(new Exiv2::XmpKey(prefix, property));
888
    } // makeXmpKey
889

    
890
}
(6-6/13)