I use XMPkit to write custom meta data for any kind of images. the source is as shown below:
-(IBAction)AddKeyword:(id)sender {
SXMPMeta::Initialize();
SXMPFiles::Initialize();
string actualPrefix;
SXMPMeta::RegisterNamespace(kXMP_NS_SDK_EDIT, "xsdkEdit", &actualPrefix);
SXMPMeta::RegisterNamespace(kXMP_NS_SDK_USERS, "xsdkUser",&actualPrefix);
const char *theString=[@"/Users/administrator/Desktop/Sample Applications/images/Blue_Lagoon-poster.jpg" UTF8String];
string filename=theString;
XMP_OptionBits opts = kXMPFiles_OpenForUpdate | kXMPFiles_OpenUseSmartHandler;
bool ok;
SXMPFiles myFile;
std::string status = "";
// First we try and open the file
ok = myFile.OpenFile(filename, kXMP_UnknownFile, opts);
if( ! ok )
{
status += "No smart handler available for " + filename + "\n";
status += "Trying packet scanning.\n";
// Now try using packet scanning
opts = kXMPFiles_OpenForUpdate | kXMPFiles_OpenUsePacketScanning;
ok = myFile.OpenFile(filename, kXMP_UnknownFile, opts);
}
cout << status << endl;
cout << filename << " is opened successfully" << endl;
// Create the xmp object and get the xmp data
//SXMPMeta meta;
//myFile.GetXMP(&meta);
string elementValue;
if(ok)
{
if(!SXMPMeta::Initialize())
{
cout << "Could not initialize Toolkit!";
}
else
{
try
{
// Register the namespaces
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, "[email protected]", 0);
meta.AppendArrayItem(kXMP_NS_SDK_EDIT, path.c_str(), 0, "[email protected]", 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");
SXMPMeta rdfMeta = createXMPFromRDF();
SXMPUtils::AppendProperties(rdfMeta, &meta, (kXMPUtil_DoAllProperties | kXMPUtil_ReplaceOldValues ));
string metaBuffer1;
// Write the packet to a file but this time as compact RDF
XMP_OptionBits outOpts = kXMP_OmitPacketWrapper | kXMP_UseCompactFormat;
meta.SerializeToBuffer(&metaBuffer1, outOpts);
// Check we can put the XMP packet back into the file
if(myFile.CanPutXMP(meta))
{
// If so then update the file with the modified XMP
myFile.PutXMP(meta);
}
// Close the SXMPFile. This *must* be called. The XMP is not
// actually written and the disk file is not closed until this call is made.
myFile.CloseFile();
// Dump the XMP object
cout << "dumping XMP object to file XMPDump1.txt" << endl;
ofstream dumpFile;
dumpFile.open("XMPDumpTest.txt", ios::out);
meta.DumpObject(DumpXMPToFile, &dumpFile);
dumpFile.close();
// Dump the namespaces to a file
cout << "dumping namespaces to file NameDump.txt" << endl;
dumpFile.open("NameDumpTest.txt", ios::out);
meta.DumpNamespaces(XMPFileDump, &dumpFile);
dumpFile.close();
}
catch(XMP_Error & e)
{
cout << "ERROR: " << e.GetErrMsg();
}
SXMPMeta::Terminate();
}
}
[arr addObject:t3];
[tableView reloadData]; }
Now to retrieve the meta data I use this code:
-(void)sourceFileValues_READ_Custom {
NSString * pathOfTheFile = @"/Users/administrator/Desktop/Sample Applications/images/Blue_Lagoon-poster.jpg";
[indicator setHidden:NO];
[indicator startAnimation:nil];
NSString *path=pathOfTheFile;
const char *theString=[path UTF8String];
string filename=theString;
// Must initialize SXMPFiles before we use it
if ( ! SXMPFiles::Initialize() )
{
cout << "Could not initialize SXMPFiles.";
}
try{
XMP_OptionBits opts = kXMPFiles_OpenForRead | kXMPFiles_OpenUseSmartHandler;
bool ok;
SXMPFiles myFile;
std::string status = "";
// First we try and open the file
ok = myFile.OpenFile(filename, kXMP_UnknownFile, opts);
if( ! ok )
{
status += "No smart handler available for " + filename + "\n";
status += "Trying packet scanning.\n";
// Now try using packet scanning
opts = kXMPFiles_OpenForUpdate | kXMPFiles_OpenUsePacketScanning;
ok = myFile.OpenFile(filename, kXMP_UnknownFile, opts);
}
// If the file is open then read the metadata
if(ok)
{
cout << status << endl;
cout << filename << " is opened successfully" << endl;
// Create the xmp object and get the xmp data
SXMPMeta meta;
myFile.GetXMP(&meta);
//bool exists;
// Read a simple property
//exists=meta.CountArrayItems(kXMP_NS_SDK_USERS, "User");
string userFieldPath;
string userItemPath;
string elementValue;
//meta.GetQualifier(kXMP_NS_SDK_EDIT, userItemPath.c_str(), kXMP_NS_SDK_USERS, "User", &elementValue, 0);
meta.GetStructField(kXMP_NS_SDK_EDIT, userItemPath.c_str(), kXMP_NS_SDK_USERS, "User", &elementValue, 0);
const char *elementValue1= elementValue.c_str();
NSString *elementvalue = [NSString stringWithUTF8String:elementValue1];
//[customkeyword addObject:@"User"];
//[customvalue addObject:elementvalue];
NSLog(@"The Value is:",elementvalue);
}
}
catch(XMP_Error & e)
{
cout << "ERROR: " << e.GetErrMsg() << endl;
}
SXMPFiles::Terminate();
SXMPMeta::Terminate();
[indicator setHidden:YES]; }
But I get this as the output:
/Users/administrator/Desktop/Sample Applications/images/Blue_Lagoon-poster.jpg is opened successfully
ERROR: Empty struct name