Copy link to clipboard
Copied
I have a image file with metadata. In Photoshop, I have a template that I pull the aforementioned image into. When I save this image within the template file (both layered and flattened), the initial metadata does not migrate over to the file.
I know there are ways to add the metadata manually prior to export, but I was wondering if there are any actions or plugins available that will make that process easy. It is different metadata every time, so I can't use the template feature.
EDIT: 23rd September... Here is an updated 1.1 version to append the keyword metadata. This assumes that the template document is the first document opened, with the two asset docs opened afterwards. It is also assumed that the asset files are flattened and don't contain layers. This also presumes that there are only two asset images in addition to the template image.
/*
Copy and paste pixel and keyword metadata.jsx
v1.1, Stephen Marsh, 23rd September 2022
https://com
...
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
...
Copy link to clipboard
Copied
You can easily add metadata using Adobe bridge. Go to the tools menu, and there you can create a Metadata template and apply that as a batch or edit metadata in files accessed by Bridge.
Copy link to clipboard
Copied
Hi Bob. Thanks for the feedback, but that does not solve the issue I'm dealing with. I'm specifically trying to migrate metadata from one file to another within the confines of Photoshop. Metadata templates don't work for us because each image has unique keywords that are done at capture.
Copy link to clipboard
Copied
A script could do this, or another option is ExifTool. In either case, the exact metadata fields would need to be known. Can you show and mark/highlight the exact fields in a screenshot from File > File Info, or raw metadata or elsewhere so that it is crystal clear which exact fields are required?
Copy link to clipboard
Copied
Copy link to clipboard
Copied
How do you "pull the image into the template"?
Is the image placed as a smart object?
Is the image open in another window and you copy/paste?
Do you use File > Open and then copy and paste?
What are the steps that you use so that a script can fit into your workflow?
Copy link to clipboard
Copied
I think there are numerous ways that our group is doing it, but lets just say for now that they are copy and pasting.
Copy link to clipboard
Copied
@Cassandra23173182ml8a wrote:
I think there are numerous ways that our group is doing it, but lets just say for now that they are copy and pasting.
As a basic start, proof of concept... Presuming that there are only two docs open and that the doc to copy the keywords and pixel data from is active:
var keys = activeDocument.info.keywords;
activeDocument.selection.selectAll();
activeDocument.activeLayer.copy();
activeDocument.close(SaveOptions.DONOTSAVECHANGES);
activeDocument.paste();
activeDocument.info.keywords = keys;
https://prepression.blogspot.com/2017/11/downloading-and-installing-adobe-scripts.html
Copy link to clipboard
Copied
Hi Stephen! Wow thanks for this -- I was able to run this script while two images were open. When ran, it took the image and the metadata of the image I was currently on, and copied it to the second image. Is there a way where I could continuously do this and append the metadata instead of replacing it?
Copy link to clipboard
Copied
I'll look into appending the metadata.
Can you explain what you mean by "continuously" in the context of this script and your workflow? Do you mean batch applying a folder of images to the template?
Copy link to clipboard
Copied
EDIT: 23rd September... Here is an updated 1.1 version to append the keyword metadata. This assumes that the template document is the first document opened, with the two asset docs opened afterwards. It is also assumed that the asset files are flattened and don't contain layers. This also presumes that there are only two asset images in addition to the template image.
/*
Copy and paste pixel and keyword metadata.jsx
v1.1, Stephen Marsh, 23rd September 2022
https://community.adobe.com/t5/photoshop-ecosystem-discussions/add-metadata-to/td-p/13196138
*/
#target photoshop;
if (documents.length === 3) {
// Set the template as the base doc
activeDocument = documents[0];
var templateDoc = activeDocument;
// Switch to the 1st asset
activeDocument = documents[1];
copyPasteImageAndMeta();
// Switch to the 2nd asset
activeDocument = documents[2];
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();
// Functions
function copyPasteImageAndMeta() {
// 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();
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);
}
} else {
alert("There should only be 3 docs open!");
}
I'm happy to make refinements/changes, I don't really know what you need so I'm just guessing!
Copy link to clipboard
Copied
Sorry, let me clarify. Say we have 3 files open in Photoshop: Photo 1, Photo 2, and Template. If I run your original code, I can only have Photo 1 and template open at the same time. I run the script on Photo 1 and it pulls the image and metadata into Template. I then open Photo 2, run the script, and while it also pulls in the metadata and image into Template, it replaces the metadata with only Photo 2's metadata, and not both.
It would be ideal to run a script in template, and for all the other files that are open, it pulls in the images and adds all the metadata for those open files without replacing one over the other. For the more recent piece of code, I get a "The command "Copy Merged" is not currently available." error, and then this error:
Error 8007: User cancelled the operation
Line: 28
-> activeDocument.activeLayer.copy(true)
Thanks so much for all your help thusfar! This is leaps and bounds more help than I've received anywhere else.
Copy link to clipboard
Copied
Ah, not knowing any better I assumed two open docs with the image active, not the template.
I'll rework it with the template being the active doc.
Edit: I have updated the previous 1.0 code to a 1.1 version to work with 3 open files – with the template being the first doc opened...
Or should the template be the last doc opened?
Are the source images being copied to the template flattened/single layer or multi-layered?
Is the template always opened first in the list of open docs, or last? Does the template have a static/unchanging name or is part of the template name unique or static compared to the 2 image files being placed into the template?
Copy link to clipboard
Copied
@Cassandra23173182ml8a – based on the feedback to my questions and your testing of the latest 1.1 version I should be able to make further refinements to the script.
Copy link to clipboard
Copied
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.
Copy link to clipboard
Copied
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);
}
}
Copy link to clipboard
Copied
Thank you Stephen!!! This is great!
Copy link to clipboard
Copied
Hi Stephen, I have small quandry that I could use help. The above code works great, but instead of layers can the new files brought into the template be brought in as Smart Objects? The rest of the workflow would remain the same.
Copy link to clipboard
Copied
@Cassandra23173182ml8a wrote:
Hi Stephen, I have small quandry that I could use help. The above code works great, but instead of layers can the new files brought into the template be brought in as Smart Objects? The rest of the workflow would remain the same.
I have updated the previous code to a new v1.3 version with a new line to convert the pasted layer to an embedded smart object layer.
If you want linked smart objects, the script would likely need a major rewrite.
Copy link to clipboard
Copied
Thank you!
Copy link to clipboard
Copied
You're welcome @Cassandra23173182ml8a !
Copy link to clipboard
Copied
Hi Stephen. I'm trying to teach myself a little bit more about how to code this so I don't need to keep pestering you!
When an image with metadata is brought into Photoshop (as a smart object or just a layer) -- does it lose it's metadata in that process? I ask because I ran into another issue. The last iteration of the scripts works very well, but I didn't think about that we might want to use the smart object layers again in a different template. What I mean is that I run the script, I make edits to one of the smart objects layers, and then I open another template. I don't want to run the script again, because I only want to bring one of the layers' metadata in. I'd like the copy the layer to the new template but obvious that doesn't bring over the metadata. But maybe there's a way how?
Copy link to clipboard
Copied
This goes back to my early questions in the thread:
"How do you "pull the image into the template"?
Is the image placed as a smart object?
Is the image open in another window and you copy/paste?
Do you use File > Open and then copy and paste?"
As the answer was the second option, the script was built around that approach. The bolded first option would have been better.
Anyway, the metadata copied from the original image was added to the target document. In a later revision to the script, the layer was turned into a smart object, but that was it.
So now the answer is pretty easy, simply edit/open the smart object, apply the metadata, close/save the smart object.
You can add the following code in the appropriate location to edit the SO:
// Edit the smart object
try {
app.runMenuItem(stringIDToTypeID('placedLayerEditContents'));
} catch (error) {
alert("The smart object can't be edited in Photoshop!");
}
Next, you would append the metadata to the open/edited smart object. Rather than duplicating the original code block it would be better to refactor it into a function, then call the function in it's original location, then also call the function while the smart object is open.
Finally, the smart object would be closed/saved:
// Close and save the smart object
activeDocument.close(SaveOptions.SAVECHANGES);
At least that is the plan, but scripting isn't always something that always goes to plan!
I am happy to make these changes for you so that you can compare the before/after, however, if you wish to work through this yourself that is great, just ask questions if you get stuck.
Copy link to clipboard
Copied
Thanks for this. I'm working with a couple people who need this functionality, and there's been additional needs that weren't fully disclosed first. Initially, I compromised with them that the open docs + template would work fine. But now they are leaning towards pulling the image in via drag and drop directly from their downloads folder. That's a big re-write to the code, I imagine?
Copy link to clipboard
Copied
It's a rewrite. The basic structure can be based off other scripts, so not too much work. Some elements can be re-used from the previous scripts (the metadata stuff).
Once all of the files have been added to the template as smart objects, run this script to loop over all smart objects and copy/append the keyword metadata to the template. Save the file as:
Append Smart Object Keyword Metadata to Template.jsx
/*
Append Smart Object Keyword Metadata to Template.jsx
v1.0, Stephen Marsh, 27th October 2022
https://community.adobe.com/t5/photoshop-ecosystem-discussions/add-metadata-to/td-p/13196138
This script presumes that there are embedded smart object layers containing keyword metadata.
The keywords in each smart object layer will be appended to the main template file as keywords.
*/
#target photoshop
if (app.documents.length) {
var myDocument = activeDocument;
myDocument.suspendHistory("Append Smart Object Keyword Metadata to Template", "processSOLayers(myDocument)");
// processSOLayers function courtesy of c.pfaffenbichler
function processSOLayers(theParent) {
for (var m = theParent.layers.length - 1; m >= 0; m--) {
var theLayer = theParent.layers[m];
// apply the function to layers;
if (theLayer.typename == "ArtLayer") {
if (theLayer.kind == LayerKind.SMARTOBJECT) {
myDocument.activeLayer = theLayer;
metaFromSO();
}
// Run on the contents of layerSets
} else {
processSOLayers(theLayer)
}
}
// 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();
return;
}
} else {
alert("A document must be open to use this script!");
}
function metaFromSO() {
// Edit the smart object
try {
app.runMenuItem(stringIDToTypeID('placedLayerEditContents'));
} catch (error) {
alert("The smart object can't be edited in Photoshop!");
}
// 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;
}
// Close the smart object without saving
activeDocument.close(SaveOptions.DONOTSAVECHANGES);
// Append the keyword metadata to the main template file
// 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();
}
Note:
There is no "de-duping" feature, so if run multiple times, previously added keywords will be added again, creating one or more duplicates. The script is only intended to be run once, at the very end of the workflow. If new files are added to the template, you will need to remove all existing keywords in the template first, before running the script again. Edit: Or run the "Keyword Optimizer" script from the Bridge Utility Script Pack from @Lumigraphics to de-dupe.