Per Layer Metadata Panel: JavaScript
The second part of the Per Layer Metadata Panel is creating a JavaScript file. The JavaScript file will have functions to load and unload the XMP Script Library to access the XMP metadata, retrieve the metadata of the active layer, set the metadata of the description and comments of the active layer, and export the metadata of the active layer. The JavaScript file will have utility functions to retrieve the date and time when the active layer was last changed, and create an XML object of the property and value to be sent to the SWF file. The JavaScript file will be created using ExtendScript Toolkit. The result is to place the JavaScript file into the Adobe Photoshop Panels folder for the SWF file to communicate with.
Go to Per Layer Metadata Panel's JavaScript API that covers a summary of functions and scripts used.
Instructions:
- Open ExtendScript Toolkit.
- Go to File > New JavaScript.
- Copy the following JavaScript code into the new file:
- Copy the following JavaScript code to retrieve the metadata into the file:
- Copy the following JavaScript code to place the metadata into the file:
- Copy the following JavaScript code to place the metadata into the file:
- Go to File > Save As....
- Under Save As:, type in PerLayerMetadata.jsx.
- Save the file into the Panels folder under the Adobe Photoshop CS5\Plug-ins\ folder located under:
- Applications for Macintosh
- Program Files for Windows
- Press Save.
- Close ExtendScript Toolkit.
function loadXMPLibrary(){
if ( !ExternalObject.AdobeXMPScript ){
try{
ExternalObject.AdobeXMPScript = new ExternalObject('lib:AdobeXMPScript');
}catch (e){
alert("Can't load XMP Script Library");
return false;
}
}
return true;
}
function unloadXMPLibrary(){
if( ExternalObject.AdobeXMPScript ) {
try{
ExternalObject.AdobeXMPScript.unload();
ExternalObject.AdobeXMPScript = undefined;
}catch (e){
alert("Can't unload XMP Script Library");
}
}
}
Code Walkthrough: The two functions, loadXMPLibrary and unloadXMPLibrary, are used respectively to load and unload the XMP Script Library to write and modify metadata. The function unloadXMPLibrary is used to free up the memory when the XMP Script Library is loaded.
function getLayerMetadata(){
var xml = "<object>";
if( app.activeDocument.activeLayer.isBackgroundLayer || !loadXMPLibrary()){
xml += convertToXML("", "layerName");
xml += convertToXML("", "layerChangedDate");
xml += convertToXML("", "description");
xml += convertToXML("", "comments");
} else {
var xmp;
try {
xml += convertToXML(app.activeDocument.activeLayer.name.toString(), "layerName");
} catch(e) {
xml += convertToXML("", "layerName");
}
try {
xml += convertToXML(getLayerChangedDate().toString(), "layerChangedDate");
} catch(e) {
xml += convertToXML("", "layerChangedDate");
}
try {
xmp = new XMPMeta(app.activeDocument.activeLayer.xmpMetadata.rawData);
} catch(e) {
xml += convertToXML("", "description");
xml += convertToXML("", "comments");
xml += "</object>";
unloadXMPLibrary();
return xml;
}
try {
xml += convertToXML(
xmp.getArrayItem(XMPConst.NS_DC, "description", 1).toString(), "description");
} catch(e) {
xml += convertToXML("", "description");
}
try {
xml += convertToXML(
xmp.getProperty(XMPConst.NS_EXIF, "userComment").toString(), "comments");
} catch(e) {
xml += convertToXML("", "comments");
}
}
xml += "</object>";
unloadXMPLibrary();
return xml;
}
Code Walkthrough: The function getLayerMetadata retrieves the metadata from the active layer selected in Adobe Photoshop. The function first makes sure that it's not a background layer and that XMP Script Library is loaded for accessing metadata. The background layer, by default, does not have metadata. The function first retrieves the layer name and the date and time the layer was last changed from the function getLayerChangeDate. The function then checks if the metadata can be successfully retrieved from the metadata. If retrieving the metadata was successful, the function retrieves the description field from the Dublin Core metadata schema represented by XMPConst.NS_DC and the comments field from the EXIF metadata schema represented by XMPConst.EXIF. For more information on other metadata schema and its corresponding fields, please refer to Adobe Developer's Center: Extensible Metadata Platform (XMP) including the XMPScript API Reference.
function setDescMetadata(desc){
if( app.activeDocument.activeLayer.isBackgroundLayer || !loadXMPLibrary()){
alert("Can't place description metadata on a background layer.\n" +
"Layer > New > Layer From Background...");
} else {
var xmp;
if (desc == "")
desc = " ";
try{
xmp = new XMPMeta(app.activeDocument.activeLayer.xmpMetadata.rawData);
} catch(e) {
xmp = new XMPMeta();
}
try{
if( xmp.countArrayItems(XMPConst.NS_DC, "description") == 0){
xmp.appendArrayItem(XMPConst.NS_DC, "description", null,
XMPConst.PROP_IS_ARRAY, XMPConst.ARRAY_IS_ORDERED);
xmp.insertArrayItem(XMPConst.NS_DC, "description", 1, desc);
} else {
xmp.setArrayItem(XMPConst.NS_DC, "description", 1, desc);
}
} catch(e) {
alert("Unable to place description metadata on selected layer.\n" + e);
}
app.activeDocument.activeLayer.xmpMetadata.rawData = xmp.serialize();
}
unloadXMPLibrary();
}
function setCommMetadata(comm){
if( app.activeDocument.activeLayer.isBackgroundLayer || !loadXMPLibrary()){
alert("Can't place comments metadata on a background layer.\n" +
"Layer > New > Layer From Background...");
} else {
var xmp;
if (comm == "")
comm = " ";
try{
xmp = new XMPMeta(app.activeDocument.activeLayer.xmpMetadata.rawData);
} catch(e) {
xmp = new XMPMeta();
}
try{
xmp.setProperty(XMPConst.NS_EXIF, "userComment", comm);
} catch(e) {
alert("Unable to place comments metadata on selected layer.\n" + e);
}
app.activeDocument.activeLayer.xmpMetadata.rawData = xmp.serialize();
}
unloadXMPLibrary();
}
Code Walkthrough: The function setDescMetadata takes the user's input metadata in the description field and apply to the description field from the Dublin Core metadata schema represented by XMPConst.NS_DC. The function setCommMetadata takes the user's input metadata in the comments field and apply to the userComment field from the EXIF metadata schema represented by XMPConst.EXIF. For more information on other metadata schema and its corresponding fields, please refer to Adobe Developer's Center: Extensible Metadata Platform (XMP) including the XMPScript API Reference.
function exportLayerMetadata(){
var path = File.saveDialog();
var file = new File(path);
file.encoding = "UTF-8";
try{
file.open("w");
file.write(app.activeDocument.activeLayer.xmpMetadata.rawData.toString());
} catch(e) {
alert("Unable to write to file.\n" + e);
}
file.close();
}
function getLayerChangedDate(){
var metadataStrID = stringIDToTypeID("metadata");
var ref = new ActionReference();
ref.putProperty(charIDToTypeID('Prpr'), metadataStrID);
ref.putEnumerated(charIDToTypeID('Lyr '), charIDToTypeID('Ordn'),
charIDToTypeID('Trgt'));
var desc = executeActionGet(ref);
if (desc.hasKey(metadataStrID)){
var descMetadata = desc.getObjectValue( metadataStrID );
var timeInSeconds = descMetadata.getDouble(stringIDToTypeID("layerTime"));
var d = new Date();
d.setTime(timeInSeconds*1000.0);
return d.toLocaleString();
}
}
function convertToXML(property, identifier){
var type = typeof property;
var xml = '<property id = "' + identifier + '" >';
switch(type){
case "number":
xml += "<number>";
xml += property.toString();
xml += "</number>";
break;
case "boolean":
xml += "<" + property.toString() + "/>";
break;
case "string":
xml += "<string>";
xml += property.toString();
xml += "</string>";
break;
case "object":
// Object case is currently not supported
alert("Object case is currently not supported");
//xml += "<object>";
//for(var i in property)
// xml += convertToXML(property[i],
//xml += "</object>";
break;
case "undefined":
xml += "<string>undefined</string>";
break;
default:
alert("Type " + type + " is unknown.");
return "";
}
xml += '</property>';
return xml;
}
Code Walkthrough: The function exportLayerMetadata prompts the user for the location to save the metadata and then write to that location. The function getLayerChangedDate retrieves the date and time in which the active layer was last changed and converts to the locale time. The function convertToXML takes a property and a unique identifier to return as an XML object understood by the Per Layer Metadata Panel.