This works so well, Stephen. Thank you so much!
Template being the first doc open makes the most sense. The source images being copied are flat! So this works perfectly for us. The template file does not have a static name, it's unique most of time.
Ok, so now you just need this to work on 2 or more open docs? There could be 2, 3 or more files open and this varies each time the script is run?
Edit: Here is an updated version that works with 2 or more open docs. There is no longer a requirement that the template is the first doc open, the active document when running the script should be the template, and the order doesn't matter. The pasted images are converted to embedded smart objects.
/*
Copy and paste pixel and keyword metadata.jsx
v1.3, Stephen Marsh, 18th October 2022
https://community.adobe.com/t5/photoshop-ecosystem-discussions/add-metadata-to/td-p/13196138
*/
#target photoshop
// Check that at least two docs are open (template + 1 file to combine)
if (documents.length >= 2) {
// Set the template doc as the target
var templateDoc = activeDocument;
// Get the operator to confirm that the active doc is the template
if (confirm("Add all open docs to the current doc?")) {
// Loop over all open files
for (var i = 0; i < app.documents.length; i++) {
activeDocument = app.documents[i];
copyPasteImageAndMeta()
}
// Remove any metabloat from the original template file
// https://prepression.blogspot.com/2017/06/metadata-bloat-photoshopdocumentancestors.html
if (ExternalObject.AdobeXMPScript === undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
var xmp = new XMPMeta(activeDocument.xmpMetadata.rawData);
xmp.deleteProperty(XMPConst.NS_PHOTOSHOP, "DocumentAncestors");
app.activeDocument.xmpMetadata.rawData = xmp.serialize();
}
} else {
alert("There must be two or more docs open to use this script!")
}
function copyPasteImageAndMeta() {
// Exclude the template doc from the loop
if (activeDocument !== templateDoc) {
// Copy the iptc keyword / dc:subject metadata
// var keys = activeDocument.info.keywords;
if (ExternalObject.AdobeXMPScript === undefined) ExternalObject.AdobeXMPScript = new ExternalObject('lib:AdobeXMPScript');
var xmp = new XMPMeta(activeDocument.xmpMetadata.rawData);
var keys = getArrayItems(XMPConst.NS_DC, 'subject');
function getArrayItems(ns, prop) {
var arrItem = [];
var items = xmp.countArrayItems(ns, prop);
for (var i = 1; i <= items; i++) {
arrItem.push(xmp.getArrayItem(ns, prop, i));
}
return arrItem;
}
//$.writeln(keys);
// Remove the file extension from the source doc name
var docName = activeDocument.name.replace(/\.[^\.]+$/, '');
// Copy the pixel data - single layer source
if (activeDocument.layers.length === 1) {
activeDocument.activeLayer.copy(false);
// Copy the pixel data - multi layer source
} else {
activeDocument.selection.selectAll();
activeDocument.activeLayer.copy(true);
}
//activeDocument.close(SaveOptions.DONOTSAVECHANGES);
// Paste the pixel data
//$.writeln(activeDocument.info.keywords);
activeDocument = templateDoc;
activeDocument.artLayers.add();
activeDocument.paste();
executeAction(stringIDToTypeID("newPlacedLayer"), undefined, DialogModes.NO);
activeDocument.activeLayer.name = docName;
// Append the keyword metadata
// activeDocument.info.keywords = keys;
// https://www.photoshopgurus.com/forum/threads/pull-layer-names-for-file-info-keywords.69966/#post-1533794558
if (!ExternalObject.AdobeXMPScript) ExternalObject.AdobeXMPScript = new ExternalObject('lib:AdobeXMPScript');
xmp = new XMPMeta(app.activeDocument.xmpMetadata.rawData);
// Uncomment the line below to clear the keywords field
//xmp.deleteProperty(XMPConst.NS_DC,'subject');
for (var s in keys) {
xmp.appendArrayItem(XMPConst.NS_DC, "subject", keys[s], 0, XMPConst.PROP_IS_ARRAY);
}
app.activeDocument.xmpMetadata.rawData = xmp.serialize();
//$.writeln(activeDocument.info.keywords);
}
}