// ================================================================================================= // 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; }