// =================================================================================================
// Copyright 2008 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.
// =================================================================================================
/**
* 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>
// 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;
}