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
|
// =================================================================================================
// Copyright 2008 Adobe
// 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.
// =================================================================================================
/**
* Tutorial solution for the Walkthrough 3 in the XMP Programmers Guide, Working with custom schema.
*
* Demonstrates how to work with a custom schema that has complex properties. It shows how to access
* and modify properties with complex paths using the path composition utilities from the XMP API
*/
#include <cstdio>
#include <vector>
#include <string>
#include <cstring>
//#define ENABLE_XMP_CPP_INTERFACE 1
// Must be defined to instantiate template classes
#define TXMP_STRING_TYPE std::string
// Ensure XMP templates are instantiated
#include "public/include/XMP.incl_cpp"
// Provide access to the API
#include "public/include/XMP.hpp"
#include <iostream>
#include <fstream>
// Made up namespace URI. Prefix will be xsdkEdit and xsdkUser
const XMP_StringPtr kXMP_NS_SDK_EDIT = "http://ns.adobe/meta/sdk/Edit/";
const XMP_StringPtr kXMP_NS_SDK_USERS = "http://ns.adobe/meta/sdk/User/";
using namespace std;
/**
* Client defined callback function to dump XMP to a file. In this case an output file stream is used
* to write a buffer, of length bufferSize, to a text file. This callback is called multiple
* times during the DumpObject() operation. See the XMP API reference for details of
* XMP_TextOutputProc() callbacks.
*/
XMP_Status XMPFileDump(void * refCon, XMP_StringPtr buffer, XMP_StringLen bufferSize)
{
XMP_Status status = 0;
try
{
ofstream * outFile = static_cast<ofstream*>(refCon);
(*outFile).write(buffer, bufferSize);
}
catch(XMP_Error & e)
{
cout << e.GetErrMsg() << endl;
return -1;
}
return status;
}
/**
* Client defined callback function to dump the registered namespaces to a file. In this case
* an output file stream is used to write a buffer, of length bufferSize, to a text file. This
* callback is called multiple times during the DumpObject() operation. See the XMP API
* reference for details of XMP_TextOutputProc() callbacks.
*/
XMP_Status DumpNS(void * refCon, XMP_StringPtr buffer, XMP_StringLen bufferSize)
{
XMP_Status status = 0;
try
{
ofstream *outFile= static_cast<ofstream*>(refCon);
(*outFile).write(buffer, bufferSize);
}
catch(XMP_Error & e)
{
cout << e.GetErrMsg() << endl;
return -1;
}
return status;
}
/**
* Writes an XMP packet in XML format to a text file
*
* rdf - a pointer to the serialized XMP
* filename - the name of the file to write to
*/
void writeRDFToFile(string * rdf, string filename)
{
ofstream outFile;
outFile.open(filename.c_str(), ios::out);
outFile << *rdf;
outFile.close();
}
/**
* Registers the namespaces that will be used with the custom schema. Then adds several new
* properties to that schema. The properties are complex, containing nested arrays and structures.
*
* XMPFiles is not used in this sample, hence no external resource is updated with the metadata. The
* created XMP object is serialized and written as RDF to a text file, the XMP object is dumped to
* a text file and the registered namespaces are also dumped to a text file.*
*
*/
int main()
{
if(!SXMPMeta::Initialize())
{
cout << "Could not initialize Toolkit!";
}
else
{
try
{
// Register the namespaces
string actualPrefix;
SXMPMeta::RegisterNamespace(kXMP_NS_SDK_EDIT, "xsdkEdit", &actualPrefix);
SXMPMeta::RegisterNamespace(kXMP_NS_SDK_USERS, "xsdkUser",&actualPrefix);
SXMPMeta meta;
// Adds a user of the document
// 1. Add a new item onto the DocumentUsers array -
// 2. Compose a path to the last element of DocumentUsers array
// 3. Add a value for the User field of the UserDetails structure
// 4. Add a qualifier to the User field. Compose the path and set the value
// 5. Add a value for the DUID field of the UserDetails structure
// 6. Add a Contact property for the ContactDetails field of the UserDetails structure
// 7. Compose a path to the ContactDetails field of the UserDetails structure.
// 8. Create the fields of the ContactDetails structure and provide values
// Create/Append the top level DocumentUsers array. If the array exists a new item will be added
meta.AppendArrayItem(kXMP_NS_SDK_EDIT, "DocumentUsers", kXMP_PropValueIsArray, 0, kXMP_PropValueIsStruct);
// Compose a path to the last item in the DocumentUsers array, this will point to a UserDetails structure
string userItemPath;
SXMPUtils::ComposeArrayItemPath(kXMP_NS_SDK_EDIT, "DocumentUsers", kXMP_ArrayLastItem, &userItemPath);
// We now have a path to the structure, so we can set the field values
meta.SetStructField(kXMP_NS_SDK_EDIT, userItemPath.c_str(), kXMP_NS_SDK_USERS, "User", "John Smith", 0);
// Add a qualifier to the User field, first compose the path to the field and then add the qualifier
string userFieldPath;
SXMPUtils::ComposeStructFieldPath(kXMP_NS_SDK_EDIT, userItemPath.c_str(), kXMP_NS_SDK_USERS, "User", &userFieldPath);
meta.SetQualifier(kXMP_NS_SDK_EDIT, userFieldPath.c_str(), kXMP_NS_SDK_USERS, "Role", "Dev Engineer");
// Compose a path to the DUID and set field value
string duidPath;
SXMPUtils::ComposeStructFieldPath(kXMP_NS_SDK_EDIT, userItemPath.c_str(), kXMP_NS_SDK_USERS, "DUID", &duidPath);
meta.SetProperty_Int(kXMP_NS_SDK_EDIT, duidPath.c_str(), 2, 0);
// Add the ContactDetails field, this field is a Contact structure
meta.SetStructField(kXMP_NS_SDK_EDIT, userItemPath.c_str(), kXMP_NS_SDK_USERS, "ContactDetails", 0, kXMP_PropValueIsStruct);
// Compose a path to the field that has the ContactDetails structure
string contactStructPath;
SXMPUtils::ComposeStructFieldPath(kXMP_NS_SDK_EDIT, userItemPath.c_str(), kXMP_NS_SDK_USERS, "ContactDetails", &contactStructPath);
// Now add the fields - all empty initially
meta.SetStructField(kXMP_NS_SDK_EDIT, contactStructPath.c_str(), kXMP_NS_SDK_USERS, "Email", 0, kXMP_PropArrayIsAlternate);
meta.SetStructField(kXMP_NS_SDK_EDIT, contactStructPath.c_str(), kXMP_NS_SDK_USERS, "Telephone", 0, kXMP_PropValueIsArray);
meta.SetStructField(kXMP_NS_SDK_EDIT, contactStructPath.c_str(), kXMP_NS_SDK_USERS, "BaseLocation", "", 0);
// Add some values for the fields
// Email: Get the path to the field named 'Email' in the ContactDetails structure and use it to append items
string path;
SXMPUtils::ComposeStructFieldPath(kXMP_NS_SDK_EDIT, contactStructPath.c_str(), kXMP_NS_SDK_USERS, "Email", &path);
meta.AppendArrayItem(kXMP_NS_SDK_EDIT, path.c_str(), 0, "js@adobe.meta.com", 0);
meta.AppendArrayItem(kXMP_NS_SDK_EDIT, path.c_str(), 0, "js@adobe.home.com", 0);
// Telephone
SXMPUtils::ComposeStructFieldPath(kXMP_NS_SDK_EDIT, contactStructPath.c_str(), kXMP_NS_SDK_USERS, "Telephone", &path);
meta.AppendArrayItem(kXMP_NS_SDK_EDIT, path.c_str(), 0, "89112", 0);
meta.AppendArrayItem(kXMP_NS_SDK_EDIT, path.c_str(), 0, "84432", 0);
// BaseLocation
SXMPUtils::ComposeStructFieldPath(kXMP_NS_SDK_EDIT, contactStructPath.c_str(), kXMP_NS_SDK_USERS, "BaseLocation", &path);
meta.SetProperty(kXMP_NS_SDK_EDIT, path.c_str(), "London", 0);
// Add a user edit
// 1. Add an item (a structure) to the DocumentEdit array
// 2. Compose a path to the last item in the DocumentEdit array
// 3. Add fields and values to the EditDetails structure
// Create the array
meta.AppendArrayItem(kXMP_NS_SDK_EDIT, "DocumentEdit", kXMP_PropArrayIsOrdered, 0, kXMP_PropValueIsStruct);
// Compose a path to the last item of the DocumentEdit array, this gives the path to the structure
string lastItemPath;
SXMPUtils::ComposeArrayItemPath(kXMP_NS_SDK_EDIT, "DocumentEdit", kXMP_ArrayLastItem, &lastItemPath);
// Add the Date field
SXMPUtils::ComposeStructFieldPath(kXMP_NS_SDK_EDIT, lastItemPath.c_str(), kXMP_NS_SDK_EDIT, "EditDate", &path);
XMP_DateTime dt;
SXMPUtils::CurrentDateTime(&dt);
meta.SetProperty_Date(kXMP_NS_SDK_EDIT, path.c_str(), dt, 0);
// Add the DUID field
SXMPUtils::ComposeStructFieldPath(kXMP_NS_SDK_EDIT, lastItemPath.c_str(), kXMP_NS_SDK_EDIT, "DUID", &path);
meta.SetProperty_Int(kXMP_NS_SDK_EDIT, path.c_str(), 2, 0);
// Add the EditComments field
SXMPUtils::ComposeStructFieldPath(kXMP_NS_SDK_EDIT, lastItemPath.c_str(), kXMP_NS_SDK_EDIT, "EditComments", &path);
meta.SetLocalizedText(kXMP_NS_SDK_EDIT, path.c_str(), "en", "en-US", "Document created.", 0);
// Add the EditTool field
meta.SetStructField(kXMP_NS_SDK_EDIT, lastItemPath.c_str(), kXMP_NS_SDK_EDIT, "EditTool", "FrameXML", 0);
// Write the RDF to a file
cout << "writing RDF to file CS_RDF.txt" << endl;
string metaBuffer;
meta.SerializeToBuffer(&metaBuffer);
writeRDFToFile(&metaBuffer, "CS_RDF.txt");
// Dump the XMP object
cout << "dumping XMP object to file XMPDump.txt" << endl;
ofstream dumpFile;
dumpFile.open("XMPDump.txt", ios::out);
meta.DumpObject(XMPFileDump, &dumpFile);
dumpFile.close();
// Dump the namespaces to a file
cout << "dumping namespaces to file NameDump.txt" << endl;
dumpFile.open("NameDump.txt", ios::out);
meta.DumpNamespaces(XMPFileDump, &dumpFile);
dumpFile.close();
}
catch(XMP_Error & e)
{
cout << "ERROR: " << e.GetErrMsg();
}
SXMPMeta::Terminate();
}
return 0;
}
|