summaryrefslogtreecommitdiff
path: root/public/include/TXMPUtils.hpp
blob: 95cdc9cc6f3ed0f59c9e230869c4b738cb9389a1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
#ifndef __TXMPUtils_hpp__
#define __TXMPUtils_hpp__ 1

#if ( ! __XMP_hpp__ )
    #error "Do not directly include, use XMP.hpp"
#endif

// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
// Copyright 2002-2007 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
// of the Adobe license agreement accompanying it.
// =================================================================================================

//  ================================================================================================
/// \file TXMPUtils.hpp
/// \brief Template class for the XMP Toolkit utility services.
///
/// \c TXMPUtils is the template class providing utility services for the XMP Toolkit. It should be
/// instantiated with a string class such as <tt>std::string</tt>. Please read the general toolkit
/// usage notes for information about the overall architecture of the XMP API.
//  ================================================================================================

//  ================================================================================================
/// \class TXMPUtils TXMPUtils.hpp
/// \brief Template class for the XMP Toolkit utility services.
///
/// \c TXMPUtils is the template class providing utility services for the XMP Toolkit. It should be
/// instantiated with a string class such as <tt>std::string</tt>. Please read the general toolkit
/// usage notes for information about the overall architecture of the XMP API.
///
/// This is a class for C++ scoping purposes only. It has only static functions, you cannot create
/// an object. These are all functions that layer cleanly on top of the core XMP toolkit. The
/// template wraps a string class around the raw XMP API, so that output strings are automatically
/// copied and access is fully thread safe.  String objects are only necessary for output strings.
/// Input strings are literals and passed as typical C <tt>const char *</tt>.
///
/// The template parameter, class \c TtStringObj, is described in the XMP.hpp umbrella header.
//  ================================================================================================

template <class tStringObj>
class TXMPUtils {

public:

    // =============================================================================================
    // No constructors or destructor declared or needed
    // ================================================

    // =============================================================================================
    // =============================================================================================

    //  ============================================================================================
    /// \name Path composition functions
    /// @{
    /// These functions provide support for composing path expressions to deeply nested properties.
    /// The functions in \c TXMPMeta such as \c GetProperty, \c GetArrayItem, and \c GetStructField
    /// provide easy access to top level simple properties, items in top level arrays, and fields
    /// of top level structs. They do not provide convenient access to more complex things like
    /// fields several levels deep in a complex struct, or fields within an array of structs, or
    /// items of an array that is a field of a struct. These functions can also be used to compose
    /// paths to top level array items or struct fields so that you can use the binary accessors
    /// like \c GetProperty_Int.
    ///
    /// You can use these functions is to compose a complete path expression, or all but the last
    /// component. Suppose you have a property that is an array of integers within a struct. You can
    /// access one of the array items like this:
    ///
    /// \verbatim
    ///  SXMPUtils::ComposeStructFieldPath ( schemaNS, "Struct", fieldNS, "Array", &path );
    ///  SXMPUtils::ComposeArrayItemPath ( schemaNS, path, index, &path );
    ///  exists = xmpObj.GetProperty_Int ( schemaNS, path, &value, &options );
    /// \endverbatim
    ///
    /// You could also use this code if you want the string form of the integer:
    ///
    /// \verbatim
    ///  SXMPUtils::ComposeStructFieldPath ( schemaNS, "Struct", fieldNS, "Array", &path );
    ///  xmpObj.GetArrayItem ( schemaNS, path, index, &value, &options );
    /// \endverbatim
    ///
    /// \note It might look confusing that the \c schemaNS is passed in all of the calls above. This
    /// is because the XMP toolkit keeps the top level "schema" namespace separate from the rest
    /// of the path expression.

    //  --------------------------------------------------------------------------------------------
    /// \brief Compose the path expression for an item in an array.
    ///
    /// \param schemaNS The namespace URI for the array. Must not be null or the empty string.
    ///
    /// \param arrayName The name of the array. May be a general path expression, must not be null
    /// or the empty string.
    ///
    /// \param itemIndex The index of the desired item. Arrays in XMP are indexed from 1. The
    /// constant \c kXMP_ArrayLastItem always refers to the last existing array item.
    ///
    /// \param fullPath A pointer to the string that will be assigned the composed path. This will
    /// be of the form <tt>ns:arrayName[i]</tt>, where "ns" is the prefix for \c schemaNS and "i"
    /// is the decimal representation of \c itemIndex. If the value of \c itemIndex is
    /// \c kXMP_ArrayLastItem, the path is <tt>ns:arrayName[last()]</tt>.

    static void
    ComposeArrayItemPath ( XMP_StringPtr schemaNS,
                           XMP_StringPtr arrayName,
                           XMP_Index     itemIndex,
                           tStringObj *  fullPath );

    //  --------------------------------------------------------------------------------------------
    /// \brief Compose the path expression for a field in a struct.
    ///
    /// \param schemaNS The namespace URI for the struct. Must not be null or the empty string.
    ///
    /// \param structName The name of the struct. May be a general path expression, must not be null
    /// or the empty string.
    ///
    /// \param fieldNS The namespace URI for the field. Must not be null or the empty string.
    ///
    /// \param fieldName The name of the field. Must be a simple XML name, must not be null or the
    /// empty string.
    ///
    /// \param fullPath A pointer to the string that will be assigned the composed path. This will
    /// be of the form <tt>ns:structName/fNS:fieldName</tt>, where "ns" is the prefix for
    /// \c schemaNS and "fNS" is the prefix for \c fieldNS.

    static void
    ComposeStructFieldPath ( XMP_StringPtr schemaNS,
                             XMP_StringPtr structName,
                             XMP_StringPtr fieldNS,
                             XMP_StringPtr fieldName,
                             tStringObj *  fullPath );

    //  --------------------------------------------------------------------------------------------
    /// \brief Compose the path expression for a qualifier.
    ///
    /// \param schemaNS The namespace URI for the property to which the qualifier is attached. Must
    /// not be null or the empty string.
    ///
    /// \param propName The name of the property to which the qualifier is attached. May be a general
    /// path expression, must not be null or the empty string.
    ///
    /// \param qualNS The namespace URI for the qualifier. May be null or the empty string if the
    /// qualifier is in the XML empty namespace.
    ///
    /// \param qualName The name of the qualifier. Must be a simple XML name, must not be null or the
    /// empty string.
    ///
    /// \param fullPath A pointer to the string that will be assigned the composed path. This will
    /// be of the form <tt>ns:propName/?qNS:qualName</tt>, where "ns" is the prefix for \c schemaNS
    /// and "qNS" is the prefix for \c qualNS.

    static void
    ComposeQualifierPath ( XMP_StringPtr schemaNS,
                           XMP_StringPtr propName,
                           XMP_StringPtr qualNS,
                           XMP_StringPtr qualName,
                           tStringObj *  fullPath );

    //  --------------------------------------------------------------------------------------------
    /// \brief Compose the path expression to select an alternate item by language.
    ///
    /// The path syntax allows two forms of "content addressing" that may be used to select an item
    /// in an array of alternatives. The form used in \c ComposeLangSelector lets you select an
    /// item in an alt-text array based on the value of its <tt>xml:lang</tt> qualifier. The other
    /// form of content addressing is shown in \c ComposeFieldSelector.
    ///
    /// \note \c ComposeLangSelector does not supplant \c SetLocalizedText or \c GetLocalizedText.
    /// They should generally be used, as they provide extra logic to choose the appropriate
    /// language and maintain consistency with the 'x-default' value. \c ComposeLangSelector gives
    /// you an path expression that is explicitly and only for the language given in the
    /// \c langName parameter.
    ///
    /// \param schemaNS The namespace URI for the array. Must not be null or the empty string.
    ///
    /// \param arrayName The name of the array. May be a general path expression, must not be null
    /// or the empty string.
    ///
    /// \param langName The RFC 3066 code for the desired language.
    ///
    /// \param fullPath A pointer to the string that will be assigned the composed path. This will
    /// be of the form <tt>ns:arrayName[\@xml:lang='langName']</tt>,
    /// where "ns" is the prefix for \c schemaNS.

    static void
    ComposeLangSelector ( XMP_StringPtr schemaNS,
                          XMP_StringPtr arrayName,
                          XMP_StringPtr langName,
                          tStringObj *  fullPath );

    //  --------------------------------------------------------------------------------------------
    /// \brief This form of \c ComposeLangSelector is a simple overload in the template that calls
    /// the above form passing <tt>langName.c_str()</tt>.

    static void
    ComposeLangSelector ( XMP_StringPtr      schemaNS,
                          XMP_StringPtr      arrayName,
                          const tStringObj & langName,
                          tStringObj *       fullPath );

    //  --------------------------------------------------------------------------------------------
    /// \brief Compose the path expression to select an alternate item by a field's value.
    ///
    /// The path syntax allows two forms of "content addressing" that may be used to select an item
    /// in an array of alternatives. The form used in \c ComposeFieldSelector lets you select an
    /// item in an array of structs based on the value of one of the fields in the structs. The
    /// other form of content addressing is shown in \c ComposeLangSelector.
    ///
    /// For example, consider a simple struct that has two fields, the name of a city and the URI
    /// of an FTP site in that city. Use this to create an array of download alternatives. You can
    /// show the user a popup built from the values of the city fields. You can then get the
    /// corresponding URI as follows:
    ///
    /// \verbatim
    ///  ComposeFieldSelector ( schemaNS, "Downloads", fieldNS, "City", chosenCity, &path );
    ///  exists = GetStructField ( schemaNS, path, fieldNS, "URI", &uri );
    /// \endverbatim
    ///
    /// \param schemaNS The namespace URI for the array. Must not be null or the empty string.
    ///
    /// \param arrayName The name of the array. May be a general path expression, must not be null
    /// or the empty string.
    ///
    /// \param fieldNS The namespace URI for the field used as the selector. Must not be null or the
    /// empty string.
    ///
    /// \param fieldName The name of the field used as the selector. Must be a simple XML name, must
    /// not be null or the empty string. It must be the name of a field that is itself simple.
    ///
    /// \param fieldValue The desired value of the field.
    ///
    /// \param fullPath A pointer to the string that will be assigned the composed path. This will
    /// be of the form <tt>ns:arrayName[fNS:fieldName='fieldValue']</tt>, where "ns" is the prefix
    /// for \c schemaNS and "fNS" is the prefix for \c fieldNS.

    static void
    ComposeFieldSelector ( XMP_StringPtr schemaNS,
                           XMP_StringPtr arrayName,
                           XMP_StringPtr fieldNS,
                           XMP_StringPtr fieldName,
                           XMP_StringPtr fieldValue,
                           tStringObj *  fullPath );

    //  --------------------------------------------------------------------------------------------
    /// \brief This form of ComposeFieldSelector is a simple overload in the template that calls the
    /// above form passing <tt>fieldValue.c_str()</tt>.

    static void
    ComposeFieldSelector ( XMP_StringPtr      schemaNS,
                           XMP_StringPtr      arrayName,
                           XMP_StringPtr      fieldNS,
                           XMP_StringPtr      fieldName,
                           const tStringObj & fieldValue,
                           tStringObj *       fullPath );

    /// @}

    // =============================================================================================
    // =============================================================================================

    //  ============================================================================================
    /// \name Binary-String conversion functions
    /// @{

    //  --------------------------------------------------------------------------------------------
    /// \brief Convert from Boolean to string.
    ///
    /// \param binValue The Boolean value to be converted.
    ///
    /// \param strValue The string representation of the Boolean. The values used are given by the
    /// macros \c kXMP_TrueStr and \c kXMP_FalseStr found in \c XMP_Const.h.

    static void
    ConvertFromBool ( bool         binValue,
                      tStringObj * strValue );

    //  --------------------------------------------------------------------------------------------
    /// \brief Convert from integer to string.
    ///
    /// \param binValue The integer value to be converted.
    ///
    /// \param format Optional C sprintf format for the conversion. Defaults to "%d".
    ///
    /// \param strValue The string representation of the integer.

    static void
    ConvertFromInt ( long          binValue,
                     XMP_StringPtr format,
                     tStringObj *  strValue );

    static void
    ConvertFromInt64 ( long long     binValue,
                       XMP_StringPtr format,
                       tStringObj *  strValue );

    //  --------------------------------------------------------------------------------------------
    /// \brief Convert from floating point to string.
    ///
    /// \param binValue The floating point value to be converted.
    ///
    /// \param format Optional C sprintf format for the conversion. Defaults to "%f".
    ///
    /// \param strValue The string representation of the floating point value.

    static void
    ConvertFromFloat ( double        binValue,
                       XMP_StringPtr format,
                       tStringObj *  strValue );

    //  --------------------------------------------------------------------------------------------
    /// \brief Convert from date/time to string.
    ///
    /// Format a date according to the ISO 8601 profile in http://www.w3.org/TR/NOTE-datetime:
    ///	YYYY
    ///	YYYY-MM
    ///	YYYY-MM-DD
    ///	YYYY-MM-DDThh:mmTZD
    ///	YYYY-MM-DDThh:mm:ssTZD
    ///	YYYY-MM-DDThh:mm:ss.sTZD
    ///
    ///	YYYY = four-digit year
    ///	MM	 = two-digit month (01=January, etc.)
    ///	DD	 = two-digit day of month (01 through 31)
    ///	hh	 = two digits of hour (00 through 23)
    ///	mm	 = two digits of minute (00 through 59)
    ///	ss	 = two digits of second (00 through 59)
    ///	s	 = one or more digits representing a decimal fraction of a second
    ///	TZD	 = time zone designator (Z or +hh:mm or -hh:mm)
    ///
    /// \note ISO 8601 does not seem to allow years less than 1000 or greater than 9999. We allow
    /// any year, even negative ones. The year is formatted as "%.4d".
    ///
    /// \note As a compatibility "tactic" (OK, a hack), so-called time-only input is allowed where
    /// the year, month, and day are all zero. This is output as "0000-00-00...".
    ///
    /// \param binValue The \c XMP_DateTime value to be converted.
    ///
    /// \param strValue The ISO 8601 string representation of the date/time.

    static void
    ConvertFromDate ( const XMP_DateTime & binValue,
                      tStringObj *         strValue );

    //  --------------------------------------------------------------------------------------------
    /// \brief Convert from string to Boolean.
    ///
    /// \param strValue The string representation of the Boolean.
    ///
    /// \result The appropriate C++ bool value for the string. The preferred strings are
    /// \c kXMP_TrueStr and \c kXMP_FalseStr. If these do not match, a case insensitive comparison is
    /// tried, then simply 't' or 'f', and finally non-zero and zero integer representations.

    static bool
    ConvertToBool ( XMP_StringPtr strValue );

    //  --------------------------------------------------------------------------------------------
    /// \brief This form of \c ConvertToBool is a simple overload in the template that calls the
    /// above form passing <tt>strValue.c_str()</tt>.

    static bool
    ConvertToBool ( const tStringObj & strValue );

    //  --------------------------------------------------------------------------------------------
    /// \brief Convert from string to integer.
    ///
    /// \param strValue The string representation of the integer.
    ///
    /// \result The integer value as a C long.

    static long
    ConvertToInt ( XMP_StringPtr strValue );

    //  --------------------------------------------------------------------------------------------
    /// \brief This form of \c ConvertToInt is a simple overload in the template that calls the above
    /// form passing <tt>strValue.c_str()</tt>.

    static long
    ConvertToInt ( const tStringObj & strValue );

    //  --------------------------------------------------------------------------------------------
    /// \brief Convert from string to 64 bit integer.
    ///
    /// \param strValue The string representation of the integer.
    ///
    /// \result The integer value as a C long long.

    static long long
    ConvertToInt64 ( XMP_StringPtr strValue );

    //  --------------------------------------------------------------------------------------------
    /// \brief This form of ConvertToInt64 is a simple overload in the template that calls the above
    /// form passing <tt>strValue.c_str()</tt>.

    static long long
    ConvertToInt64 ( const tStringObj & strValue );

    //  --------------------------------------------------------------------------------------------
    /// \brief Convert from string to floating point.
    ///
    /// \param strValue The string representation of the floating point value.
    ///
    /// \result The floating point value.

    static double
    ConvertToFloat ( XMP_StringPtr strValue );

    //  --------------------------------------------------------------------------------------------
    /// \brief This form of \c ConvertToFloat is a simple overload in the template that calls the
    /// above form passing <tt>strValue.c_str()</tt>.

    static double
    ConvertToFloat ( const tStringObj & strValue );

    //  --------------------------------------------------------------------------------------------
    /// \brief Convert from string to date/time.
    ///
    /// Parse a date according to the ISO 8601 profile in http://www.w3.org/TR/NOTE-datetime:
    ///	YYYY
    ///	YYYY-MM
    ///	YYYY-MM-DD
    ///	YYYY-MM-DDThh:mmTZD
    ///	YYYY-MM-DDThh:mm:ssTZD
    ///	YYYY-MM-DDThh:mm:ss.sTZD
    ///
    ///	YYYY = four-digit year
    ///	MM	 = two-digit month (01=January, etc.)
    ///	DD	 = two-digit day of month (01 through 31)
    ///	hh	 = two digits of hour (00 through 23)
    ///	mm	 = two digits of minute (00 through 59)
    ///	ss	 = two digits of second (00 through 59)
    ///	s	 = one or more digits representing a decimal fraction of a second
    ///	TZD	 = time zone designator (Z or +hh:mm or -hh:mm)
    ///
    /// \note ISO 8601 does not seem to allow years less than 1000 or greater than 9999. We allow
    /// any year, even negative ones. The year is assumed to be formatted as "%.4d".
    ///
    /// \note As compatibility "tactics" (OK, hacks), a missing date portion or missing TZD are
    /// tolerated. A missing date value may begin with "Thh:" or "hh:"; the year, month, and day are
    /// all set to zero in the XMP_DateTime value. A missing TZD is assumed to be UTC.
    ///
    /// \param strValue The ISO 8601 string representation of the date/time.
    ///
    /// \param binValue A pointer to the \c XMP_DateTime variable to be assigned the date/time components.

    static void
    ConvertToDate ( XMP_StringPtr  strValue,
                    XMP_DateTime * binValue );

    //  --------------------------------------------------------------------------------------------
    /// \brief This form of \c ConvertToDate is a simple overload in the template that calls the above
    /// form passing s<tt>strValue.c_str()</tt>.

    static void
    ConvertToDate ( const tStringObj & strValue,
                    XMP_DateTime *     binValue );

    /// @}

    // =============================================================================================
    // =============================================================================================

    //  ============================================================================================
    /// \name Date/Time functions
    /// @{

    //  --------------------------------------------------------------------------------------------
    /// \brief Obtain the current date and time.
    ///
    /// \param time	A pointer to the \c XMP_DateTime variable to be assigned the current date
    /// and time. The returned time is UTC, properly adjusted for the local time zone. The
    /// resolution of the time is not guaranteed to be finer than seconds.

    static void
    CurrentDateTime ( XMP_DateTime * time );

    //  --------------------------------------------------------------------------------------------
    /// \brief Set the local time zone.
    ///
    /// \param time	A pointer to the \c XMP_DateTime variable containing the value to be modified. Any
    /// existing time zone value is replaced, the other date/time fields are not adjusted in any way.

    static void
    SetTimeZone ( XMP_DateTime * time );

    //  --------------------------------------------------------------------------------------------
    /// \brief Make sure a time is UTC.
    ///
    /// \param time	A pointer to the \c XMP_DateTime variable containing the time to be modified. If
    /// the time zone is not UTC, the time is adjusted and the time zone set to be UTC.

    static void
    ConvertToUTCTime ( XMP_DateTime * time );

    //  --------------------------------------------------------------------------------------------
    /// \brief Make sure a time is local.
    ///
    /// \param time	A pointer to the \c XMP_DateTime variable containing the time to be modified. If
    /// the time zone is not the local zone, the time is adjusted and the time zone set to be local.

    static void
    ConvertToLocalTime ( XMP_DateTime * time );

    //  --------------------------------------------------------------------------------------------
    /// \brief Compare the order of two date/time values.
    ///
    /// \param left The "lefthand" date/time.
    ///
    /// \param right The "righthand" date/time.
    ///
    /// \result
    /// \li -1 if left is before right
    /// \li 0 if left matches right
    /// \li +1 if left is after right

    static int
    CompareDateTime ( const XMP_DateTime & left,
                      const XMP_DateTime & right );

    /// @}

    // =============================================================================================
    // =============================================================================================

    //  ============================================================================================
    /// \name Base 64 Encoding and Decoding
    /// @{

    //  --------------------------------------------------------------------------------------------
    /// \brief Convert from raw data to Base64 encoded string.
    ///
    /// \param rawStr The pointer to raw data to be converted.
    ///
    /// \param rawLen The length of raw data to be converted.
    ///
    /// \param encodedStr The XMP object to contain the encoded string.

    static void
    EncodeToBase64 ( XMP_StringPtr rawStr,
                     XMP_StringLen rawLen,
                     tStringObj *  encodedStr );

    //  --------------------------------------------------------------------------------------------
    /// \brief This form of \c EncodeToBase64 is a simple overload in the template that calls the
    /// above form passing <tt>rawStr.c_str()</tt>, and <tt>rawStr.size()</tt>.

    static void
    EncodeToBase64 ( const tStringObj & rawStr,
                     tStringObj *       encodedStr );

    //  --------------------------------------------------------------------------------------------
    /// \brief Decode from Base64 encoded string to raw data.
    ///
    /// \param encodedStr The pointer to encoded data to be converted.
    ///
    /// \param encodedLen The length of encoded datavto be converted.
    ///
    /// \param rawStr The XMP object to contain the decoded string.

    static void
    DecodeFromBase64 ( XMP_StringPtr encodedStr,
                       XMP_StringLen encodedLen,
                       tStringObj *  rawStr );

    //  --------------------------------------------------------------------------------------------
    /// \brief This form of \c DecodeFromBase64 is a simple overload in the template that calls the
    /// above form passing <tt>encodedStr.c_str()</tt>, and <tt>encodedStr.size()</tt>.

    static void
    DecodeFromBase64 ( const tStringObj & encodedStr,
                       tStringObj *       rawStr );

    /// @}

    // =============================================================================================
    // =============================================================================================

    //  ============================================================================================
    /// \name JPEG file handling
    /// @{
    /// These functions support the partitioning of XMP in JPEG files into standard and extended 
    /// portions in order to work around the 64KB size limit of JPEG marker segments.

    //  --------------------------------------------------------------------------------------------
    /// \brief Create XMP serializations appropriate for a JPEG file. The standard XMP in a JPEG
    /// file is limited to about 65500 bytes. \c PackageForJPEG attempts to fit the serialization
    /// within that limit. If necessary it will partition the XMP into 2 serializations.
    ///
    /// \param xmpObj The XMP for the JPEG file.
    ///
    /// \param standardXMP The full standard XMP packet.
    ///
    /// \param extendedXMP The serialized extended XMP, empty if not needed.
    ///
    /// \param extendedDigest An MD5 digest of the serialized extended XMP, empty if not needed.

    static void
    PackageForJPEG ( const TXMPMeta<tStringObj> & xmpObj,
                     tStringObj * standardXMP,
                     tStringObj * extendedXMP,
                     tStringObj * extendedDigest );

    //  --------------------------------------------------------------------------------------------
    /// \brief Put the extended XMP properties back into the full XMP.
    ///
    /// \param fullXMP The full XMP, presumed to be initialized from the standard XMP packet.
    ///
    /// \param extendedXMP The properties that were partitioned into the extended XMP.

    static void
    MergeFromJPEG ( TXMPMeta<tStringObj> * fullXMP,
                    const TXMPMeta<tStringObj> & extendedXMP );

    /// @}

    // =============================================================================================
    // =============================================================================================

    //  ============================================================================================
    /// \name UI helper functions
    /// @{
    /// These functions are mainly of interest in implementing a user interface for editing XMP.

    //  --------------------------------------------------------------------------------------------
    /// \brief Create a single edit string from an array of strings.
    ///
    /// TBD - needs more description
    ///
    /// \param xmpObj The XMP object containing the array to be catenated.
    ///
    /// \param schemaNS The schema namespace URI for the array. Must not be null or the empty string.
    ///
    /// \param arrayName The name of the array. May be a general path expression, must not be null
    /// or the empty string. Each item in the array must be a simple string value.
    ///
    /// \param separator The string to be used to separate the items in the catenated string.
    /// Defaults to "; ", ASCII semicolon and space (U+003B, U+0020).
    ///
    /// \param quotes The characters to be used as quotes around array items that contain a separator.
    /// Defaults to '"', ASCII quote (U+0022).
    ///
    /// \param options Option flags to control the catenation.
    ///
    /// \param catedStr A pointer to the string to be assigned the catenated array items.

    static void
    CatenateArrayItems ( const TXMPMeta<tStringObj> & xmpObj,
                         XMP_StringPtr  schemaNS,
                         XMP_StringPtr  arrayName,
                         XMP_StringPtr  separator,
                         XMP_StringPtr  quotes,
                         XMP_OptionBits options,
                         tStringObj *   catedStr );

    //  --------------------------------------------------------------------------------------------
    /// \brief Separate a single edit string into an array of strings.
    ///
    /// TBD - needs more description
    ///
    /// \param xmpObj The XMP object containing the array to be updated.
    ///
    /// \param schemaNS The schema namespace URI for the array. Must not be null or the empty string.
    ///
    /// \param arrayName The name of the array. May be a general path expression, must not be null
    /// or the empty string. Each item in the array must be a simple string value.
    ///
    /// \param options Option flags to control the separation.
    ///
    /// \param catedStr The string to be separated into the array items.

    static void
    SeparateArrayItems ( TXMPMeta<tStringObj> * xmpObj,
                         XMP_StringPtr  schemaNS,
                         XMP_StringPtr  arrayName,
                         XMP_OptionBits options,
                         XMP_StringPtr  catedStr );

    //  --------------------------------------------------------------------------------------------
    /// \brief This form of \c SeparateArrayItems is a simple overload in the template that calls
    /// the aboveform passing <tt>catedStr.c_str()</tt>.

    static void
    SeparateArrayItems ( TXMPMeta<tStringObj> * xmpObj,
                         XMP_StringPtr      schemaNS,
                         XMP_StringPtr      arrayName,
                         XMP_OptionBits     options,
                         const tStringObj & catedStr );

    //  --------------------------------------------------------------------------------------------
    /// \brief Remove multiple properties from an XMP object.
    ///
    /// \c RemoveProperties was created to support the File Info dialog's Delete button, and has
    /// been been generalized somewhat from those specific needs. It operates in one of three main
    /// modes depending on the schemaNS and propName parameters:
    ///
    /// \li Non-empty \c schemaNS and \c propName - The named property is removed if it is an
    /// external property, or if the \c kXMPUtil_DoAllProperties option is passed. It does not
    /// matter whether the named property is an actual property or an alias.
    ///
    /// \li Non-empty \c schemaNS and empty \c propName - The all external properties in the named
    /// schema are removed. Internal properties are also removed if the \c kXMPUtil_DoAllProperties
    /// option is passed. In addition, aliases from the named schema will be removed if the \c
    /// kXMPUtil_IncludeAliases option is passed.
    ///
    /// \li Empty \c schemaNS and empty \c propName - All external properties in all schema are
    /// removed. Internal properties are also removed if the \c kXMPUtil_DoAllProperties option is
    /// passed. Aliases are implicitly handled because the associated actuals are.
    ///
    /// It is an error to pass and empty schemaNS and non-empty propName.
    ///
    /// \param xmpObj The XMP object containing the properties to be removed.
    ///
    /// \param schemaNS Optional schema namespace URI for the properties to be removed.
    ///
    /// \param propName Optional path expression for the property to be removed.
    ///
    /// \param options Option flags to control the deletion. The defined flags are:
    /// \li \c kXMPUtil_DoAllProperties - Do internal properties in addition to external properties.
    /// \li \c kXMPUtil_IncludeAliases - Include aliases in the "named schema" case above.

    static void
    RemoveProperties ( TXMPMeta<tStringObj> * xmpObj,
                       XMP_StringPtr  schemaNS = 0,
                       XMP_StringPtr  propName = 0,
                       XMP_OptionBits options = 0 );

    //  --------------------------------------------------------------------------------------------
    /// \brief Append properties from one XMP object to another.
    ///
    /// \c AppendProperties was created to support the File Info dialog's Append button, and has
    /// been been generalized somewhat from those specific needs. It appends information from one
    /// XMP object (\c source) to another (\c dest). The default operation is to append only external
    /// properties that do not already exist in the destination. The kXMPUtil_DoAllProperties option
    /// can be used to operate on all properties, external and internal. The kXMPUtil_ReplaceOldValues
    /// option can be used to replace the values of existing properties. The notion of external
    /// versus internal applies only to top level properties. The keep-or-replace-old notion applies
    /// within structs and arrays as described below.
    ///
    /// If kXMPUtil_ReplaceOldValues is passed then the processing is restricted to the top level
    /// properties. The processed properties from the source (according to kXMPUtil_DoAllProperties)
    /// are propagated to the destination, replacing any existing values. Properties in the destination
    /// that are not in the source are left alone.
    ///
    /// If kXMPUtil_ReplaceOldValues is not passed then the processing is more complicated. Top level
    /// properties are added to the destination if they do not already exist. If they do exist but
    /// differ in form (simple/struct/array) then the destination is left alone. If the forms match,
    /// simple properties are left unchanged while structs and arrays are merged.
    ///
    /// If kXMPUtil_DeleteEmptyValues is passed then an empty value in the source XMP causes the
    /// corresponding Dest XMP property to be deleted. The default is to treat empty values the same
    /// as non-empty values. An empty value is any of a simple empty string, an array with no items,
    /// or a struct with no fields. Qualifiers are ignored.
    ///
    /// The detailed behavior is defined by the following pseudo-code:
    /// \verbatim
    ///   AppendProperties ( sourceXMP, destXMP, options ):
    ///      doAll = options & kXMPUtil_DoAllProperties
    ///      replaceOld = options & kXMPUtil_ReplaceOldValues
    ///      deleteEmpty = options & kXMPUtil_DeleteEmptyValues
    ///      for all source schema (top level namespaces):
    ///         for all top level properties in sourceSchema:
    ///            if doAll or prop is external:
    ///               AppendSubtree ( sourceNode, destSchema, replaceOld, deleteEmpty )
    ///
    ///   AppendSubtree ( sourceNode, destParent, replaceOld, deleteEmpty ):
    ///      if deleteEmpty and source value is empty:
    ///         delete the corresponding child from destParent
    ///      else if sourceNode not in destParent (by name):
    ///         copy sourceNode's subtree to destParent
    ///      else if replaceOld:
    ///         delete subtree from destParent
    ///         copy sourceNode's subtree to destParent
    ///      else:
    ///         // Already exists in dest and not replacing, merge structs and arrays
    ///         if sourceNode and destNode forms differ:
    ///            return, leave the destNode alone
    ///         else if form is a struct:
    ///            for each field in sourceNode:
    ///               AppendSubtree ( sourceNode.field, destNode, replaceOld )
    ///         else if form is an alt-text array:
    ///            copy new items by xml:lang value into the destination
    ///         else if form is an array:
    ///            copy new items by value into the destination, ignoring order and duplicates
    /// \endverbatim
    ///
    /// \note \c AppendProperties can be expensive if replaceOld is not passed and the XMP contains
    /// large arrays. The array item checking described above is n-squared. Each source item is
    /// checked to see if it already exists in the destination, without regard to order or duplicates.
    /// Simple items are compared by value and xml:lang qualifier, other qualifiers are ignored.
    /// Structs are recursively compared by field names, without regard to field order. Arrays are
    /// compared by recursively comparing all items.
    ///
    /// \param source The source XMP object.
    ///
    /// \param dest The destination XMP object.
    ///
    /// \param options Option flags to control the copying.
    /// \li \c kXMPUtil_DoAllProperties - Do internal properties in addition to external properties.
    /// \li \c kXMPUtil_ReplaceOldValues - Replace the values of existing properties.
    /// \li \c kXMPUtil_DeleteEmptyValues - Delete properties if the new value is empty.

    static void
    AppendProperties ( const TXMPMeta<tStringObj> & source,
                       TXMPMeta<tStringObj> *       dest,
                       XMP_OptionBits options = 0 );

    //  --------------------------------------------------------------------------------------------
    /// \brief Replicate a subtree from one XMP object into another, possibly at a different location.
    ///
    /// TBD - needs more description
    ///
    /// \param source The source XMP object.
    ///
    /// \param dest The destination XMP object.
    ///
    /// \param sourceNS The schema namespace URI for the source subtree.
    ///
    /// \param sourceRoot The root location for the source subtree. May be a general path expression,
    /// must not be null or the empty string.
    ///
    /// \param destNS The schema namespace URI for the destination. Defaults to the source namespace.
    ///
    /// \param destRoot The root location for the destination. May be a general path expression.
    /// Defaults to the source location.
    ///
    /// \param options Option flags to control the separation.

    static void
    DuplicateSubtree ( const TXMPMeta<tStringObj> & source,
                       TXMPMeta<tStringObj> *       dest,
                       XMP_StringPtr  sourceNS,
                       XMP_StringPtr  sourceRoot,
                       XMP_StringPtr  destNS = 0,
                       XMP_StringPtr  destRoot = 0,
                       XMP_OptionBits options = 0 );


    /// @}

    // =============================================================================================

};  // class TXMPUtils

// =================================================================================================

#endif // __TXMPUtils_hpp__