Copy link to clipboard
Copied
A while back @Stephen Marsh created a script for me to import Remove.bg files as layers including a layermask into a template file. At that time, and for that particular case, making the image a smart object was not desirable. It would interfere with the warping process. See the original post here (including examples)
But now I want to re-use the script for a slightly different flow and wish to have the imported layer be a smart object of the original image and, on top of it, the layer mask.
Is somebody or perhaps the master@Stephen Marsh himself 😅 Able to assist me?
/*
Stack Masked Doc Layers to Template Doc.jsx
v1.0 - 21st May 2024, Stephen Marsh
https://community.adobe.com/t5/photoshop-ecosystem-discussions/script-to-import-warp-resize-and-move-layers/td-p/14623443
*/
#target photoshop
function main() {
(function () {
try {
var savedRuler = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
// Set the active doc as the template doc
var templateDoc = app.activeDocument;
var templateDocName = app.activeDocument.name;
// Ensure that the contents of the PHOTOS group is active
if (app.activeDocument.activeLayer.name != 'PHOTOS') {
app.activeDocument.activeLayer = app.activeDocument.layerSets.getByName('PHOTOS').layers[0];
}
// Select the 'remove.bg' PSD source files
var selectFile = File.openDialog('Select the file/s:', Multiselect = true);
if (selectFile === null) {
return;
}
// Loop over the selected files
for (var i = 0; i < selectFile.length; i++) {
// Open each selected file
app.open(File(selectFile[i]));
// Set the sourceDoc as the active document
var sourceDoc = app.activeDocument;
// Select the source layer
//app.activeDocument.activeLayer = app.activeDocument.layers.getByName('remove.bg');
app.activeDocument.activeLayer = app.activeDocument.layers[0];
// Ensure that the RGB channels are selected and not the layer mask
app.activeDocument.activeChannels = app.activeDocument.componentChannels;
// Apply the Background to the remove.bg layer
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
var descriptor2 = new ActionDescriptor();
var reference = new ActionReference();
reference.putEnumerated( s2t( "channel" ), s2t( "channel" ), s2t( "RGB" ));
reference.putName( s2t( "layer" ), "Background" );
descriptor2.putReference( s2t( "to" ), reference );
descriptor.putObject( s2t( "with" ), s2t( "calculation" ), descriptor2 );
executeAction( s2t( "applyImageEvent" ), descriptor, DialogModes.NO );
// Duplicate the source layer to the template doc
dupeLayer(templateDocName, app.activeDocument.name.replace(/\.[^\.]+$/, ''));
// Set the template doc as the active document
app.activeDocument = templateDoc;
// Conditionally resize the layer to fit the canvas
var docWidth = app.activeDocument.width;
var docHeight = app.activeDocument.height;
var layerWidth = (app.activeDocument.activeLayer.bounds[2].value - app.activeDocument.activeLayer.bounds[0].value);
var layerHeight = (app.activeDocument.activeLayer.bounds[3].value - app.activeDocument.activeLayer.bounds[1].value);
if (layerWidth > docWidth) {
//alert('The layer is wider than the canvas!');
app.activeDocument.activeLayer.resize((docWidth / layerWidth) * 100, (docWidth / layerWidth) * 100, AnchorPosition.MIDDLECENTER);
}
if (layerHeight > docHeight) {
//alert('The layer is taller than the canvas!');
app.activeDocument.activeLayer.resize((docHeight / layerHeight) * 100, (docHeight / layerHeight) * 100, AnchorPosition.MIDDLECENTER);
}
// Centre the duped layer on the template canvas
app.activeDocument.activeLayer.translate(docWidth / 2 - (app.activeDocument.activeLayer.bounds[0] + app.activeDocument.activeLayer.bounds[2]) / 2,
docHeight / 2 - (app.activeDocument.activeLayer.bounds[1] + app.activeDocument.activeLayer.bounds[3]) / 2);
// Close the source doc
sourceDoc.close(SaveOptions.DONOTSAVECHANGES);
}
app.preferences.rulerUnits = savedRuler;
// Functions
function dupeLayer(targetDocName, sourceLayerName) {
function s2t(s) {
return app.stringIDToTypeID(s);
}
var descriptor = new ActionDescriptor();
var list = new ActionList();
var reference = new ActionReference();
var reference2 = new ActionReference();
reference.putEnumerated(s2t("layer"), s2t("ordinal"), s2t("targetEnum"));
descriptor.putReference(s2t("null"), reference);
reference2.putName(s2t("document"), targetDocName);
descriptor.putReference(s2t("to"), reference2);
descriptor.putString(s2t("name"), sourceLayerName);
descriptor.putList(s2t("ID"), list);
executeAction(s2t("duplicate"), descriptor, DialogModes.NO);
}
} catch (e) {
alert('Error!' + '\r' + e + ', Line: ' + e.line);
}
})();
}
main();
Copy link to clipboard
Copied
Try the following version:
/*
Stack Masked Doc Layers as Smart Objects to Template Doc.jsx
v1.0 - 25th June 2024, Stephen Marsh
https://community.adobe.com/t5/photoshop-ecosystem-discussions/script-to-import-warp-resize-and-move-layers/td-p/14623443
*/
#target photoshop
if (app.documents.length) {
(function () {
if (app.activeDocument.name == 'Framefoto-template.psd') {
main();
} else {
var theConfirmation = (confirm("The active doc isn't named 'Framefoto-template.psd' Continue?", false));
if (theConfirmation === false) {
return;
}
main();
}
})();
} else {
alert('A document must be open to run this script!');
}
function main() {
(function () {
try {
var savedRuler = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
// Set the active doc as the template doc
var templateDoc = app.activeDocument;
var templateDocName = app.activeDocument.name;
// Ensure that the contents of the FOTO group is active
if (app.activeDocument.activeLayer.name != 'FOTOS') {
app.activeDocument.activeLayer = app.activeDocument.layerSets.getByName('FOTOS').layers[0];
}
// Select the 'remove.bg' PSD source files
var selectFile = File.openDialog('Select the file/s:', Multiselect = true);
if (selectFile === null) {
return;
}
// Start processing
app.togglePalettes();
// Loop over the selected files
for (var i = 0; i < selectFile.length; i++) {
// Open each selected file
app.open(File(selectFile[i]));
// Set the sourceDoc as the active document
var sourceDoc = app.activeDocument;
///// 25th June 2024 - Convert the 'remove.bg' layer stack to a smart object /////
if (app.activeDocument.layers[app.activeDocument.layers.length - 1].isBackgroundLayer) {
app.activeDocument.layers[app.activeDocument.layers.length - 1].name = 'Background';
}
app.activeDocument.layers[app.activeDocument.layers.length - 1].allLocked = false;
app.runMenuItem(stringIDToTypeID('selectAllLayers'));
executeAction(stringIDToTypeID("newPlacedLayer"), undefined, DialogModes.NO);
app.runMenuItem(stringIDToTypeID('placedLayerEditContents'));
app.activeDocument.layers[app.activeDocument.layers.length - 1].allLocked = true;
app.activeDocument.layers[app.activeDocument.layers.length - 1].visible = false;
activeDocument.close(SaveOptions.SAVECHANGES);
/////
// Duplicate the source layer to the template doc
dupeLayer(templateDocName, app.activeDocument.name.replace(/\.[^\.]+$/, ''));
// Set the template doc as the active document
app.activeDocument = templateDoc;
// Conditionally resize the layer to fit the canvas
var docWidth = app.activeDocument.width;
var docHeight = app.activeDocument.height;
var layerWidth = (app.activeDocument.activeLayer.bounds[2].value - app.activeDocument.activeLayer.bounds[0].value);
var layerHeight = (app.activeDocument.activeLayer.bounds[3].value - app.activeDocument.activeLayer.bounds[1].value);
if (layerWidth > docWidth) {
//alert('The layer is wider than the canvas!');
app.activeDocument.activeLayer.resize((docWidth / layerWidth) * 100, (docWidth / layerWidth) * 100, AnchorPosition.MIDDLECENTER);
}
if (layerHeight > docHeight) {
//alert('The layer is taller than the canvas!');
app.activeDocument.activeLayer.resize((docHeight / layerHeight) * 100, (docHeight / layerHeight) * 100, AnchorPosition.MIDDLECENTER);
}
// Centre the duped layer on the template canvas
app.activeDocument.activeLayer.translate(docWidth / 2 - (app.activeDocument.activeLayer.bounds[0] + app.activeDocument.activeLayer.bounds[2]) / 2,
docHeight / 2 - (app.activeDocument.activeLayer.bounds[1] + app.activeDocument.activeLayer.bounds[3]) / 2);
// Close the source doc
sourceDoc.close(SaveOptions.DONOTSAVECHANGES);
}
app.preferences.rulerUnits = savedRuler;
// End of processing
app.togglePalettes();
app.beep();
// Functions
function dupeLayer(targetDocName, sourceLayerName) {
function s2t(s) {
return app.stringIDToTypeID(s);
}
var descriptor = new ActionDescriptor();
var list = new ActionList();
var reference = new ActionReference();
var reference2 = new ActionReference();
reference.putEnumerated(s2t("layer"), s2t("ordinal"), s2t("targetEnum"));
descriptor.putReference(s2t("null"), reference);
reference2.putName(s2t("document"), targetDocName);
descriptor.putReference(s2t("to"), reference2);
descriptor.putString(s2t("name"), sourceLayerName);
descriptor.putList(s2t("ID"), list);
executeAction(s2t("duplicate"), descriptor, DialogModes.NO);
}
function selectAllLayers() {
var c2t = function (s) {
return app.charIDToTypeID(s);
};
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
var descriptor2 = new ActionDescriptor();
var reference = new ActionReference();
var reference2 = new ActionReference();
reference2.putEnumerated(s2t("layer"), s2t("ordinal"), s2t("targetEnum"));
descriptor.putReference(c2t("null"), reference2);
executeAction(s2t("selectAllLayers"), descriptor, DialogModes.NO);
try {
// Add the Background layer if it exists
reference.putProperty(s2t("layer"), s2t("background"));
descriptor2.putReference(c2t("null"), reference);
descriptor2.putEnumerated(s2t("selectionModifier"), s2t("selectionModifierType"), s2t("addToSelection"));
descriptor2.putBoolean(s2t("makeVisible"), false);
executeAction(s2t("select"), descriptor2, DialogModes.NO);
} catch (e) {
alert("Error!" + "\r" + e + ' ' + e.line);
}
}
} catch (e) {
alert('Error!' + '\r' + e + ', Line: ' + e.line);
}
})();
}
Copy link to clipboard
Copied
Thanks, @Stephen Marsh
It does work, but I was looking to keep the layer masks and have these on top of the smart object.
I was able to get it to work using your initial code, ChatGPT, and some Frankenstein assembly 🫣
I did remove
/*
25-06-2024 Stack Masked Doc Layers to Template Doc by Stephen Marsh, mailesther010@gmail.com and ChatGPT
https://community.adobe.com/t5/photoshop-ecosystem-discussions/importing-remove-bg-files-as-smart-object-layer-including-the-layermask/m-p/14700531#M817361
*/
function main() {
(function () {
try {
var savedRuler = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
// Set the active doc as the template doc
var templateDoc = app.activeDocument;
var templateDocName = app.activeDocument.name;
// Ensure that the contents of the PHOTOS group is active
if (app.activeDocument.activeLayer.name != 'PHOTOS') {
app.activeDocument.activeLayer = app.activeDocument.layerSets.getByName('PHOTOS').layers[0];
}
// Select the 'remove.bg' PSD source files
var selectFile = File.openDialog('Select the file/s:', Multiselect = true);
if (selectFile === null) {
return;
}
// Loop over the selected files
for (var i = 0; i < selectFile.length; i++) {
// Open each selected file
app.open(File(selectFile[i]));
// Set the sourceDoc as the active document
var sourceDoc = app.activeDocument;
var firstLayer = sourceDoc.layers[0];;
var secondLayer = sourceDoc.layers[1];
// Select and convert the second layer to a smart object
sourceDoc.activeLayer = secondLayer
makeSmartObject();
// Copy the layer mask from the first layer and apply it to the second layer
sourceDoc.activeLayer = firstLayer
moveLayerMask("remove.bg", "remove.bg - Original Image");
// Duplicate layer to the template doc
app.activeDocument.activeLayer = app.activeDocument.layers[1];
dupeLayer(templateDocName, app.activeDocument.name.replace(/\.[^\.]+$/, ''));
// Set the template doc as the active document
app.activeDocument = templateDoc;
// Deselect the selection
app.activeDocument.selection.deselect();
// Close the source doc
sourceDoc.close(SaveOptions.DONOTSAVECHANGES);
}
app.preferences.rulerUnits = savedRuler;
// Functions
function dupeLayer(targetDocName, sourceLayerName) {
function s2t(s) {
return app.stringIDToTypeID(s);
}
var descriptor = new ActionDescriptor();
var list = new ActionList();
var reference = new ActionReference();
var reference2 = new ActionReference();
reference.putEnumerated(s2t("layer"), s2t("ordinal"), s2t("targetEnum"));
descriptor.putReference(s2t("null"), reference);
reference2.putName(s2t("document"), targetDocName);
descriptor.putReference(s2t("to"), reference2);
descriptor.putString(s2t("name"), sourceLayerName);
descriptor.putList(s2t("ID"), list);
executeAction(s2t("duplicate"), descriptor, DialogModes.NO);
}
function makeSmartObject() {
var idnewPlacedLayer = stringIDToTypeID("newPlacedLayer");
executeAction(idnewPlacedLayer, undefined, DialogModes.NO);
}
function moveLayerMask(sourceLayerName, targetLayerName) {
function cTID(s) { return app.charIDToTypeID(s); };
var desc8 = new ActionDescriptor();
desc8.putClass(cTID('Nw '), cTID('Chnl'));
var ref5 = new ActionReference();
ref5.putEnumerated(cTID('Chnl'), cTID('Chnl'), cTID('Msk '));
ref5.putName(cTID('Lyr '), "remove.bg - Original Image");
desc8.putReference(cTID('At '), ref5);
var ref6 = new ActionReference();
ref6.putEnumerated(cTID('Chnl'), cTID('Chnl'), cTID('Msk '));
ref6.putEnumerated(cTID('Lyr '), cTID('Ordn'), cTID('Trgt'));
desc8.putReference(cTID('Usng'), ref6);
executeAction(cTID('Mk '), desc8, DialogModes.NO);
}
function stringIDToTypeID(str) {
return app.stringIDToTypeID(str);
}
} catch (e) {
alert('Error!' + '\r' + e + ', Line: ' + e.line);
}
})();
}
main();
Copy link to clipboard
Copied
Thanks, @Stephen Marsh
It does work, but I was looking to keep the layer masks and have these on top of the smart object.
My bad, I missed that.
Thanks, @Stephen Marsh
It does work, but I was looking to keep the layer masks and have these on top of the smart object.
I was able to get it to work using your initial code, ChatGPT, and some Frankenstein assembly 🫣
I did remove
- #target photoshop - For some reason, my Visual Studio always sees this as an error. I'm curious as to why it is always used, though.
The #target app/version is a throwback to .jsx scripts being associated with the old ESTK app, so that you can have your cake and eat it (having the script passed onto say Photoshop even though it was opened by the ESTK app). You are correct that VS Code doesn't like it. I usually just // comment it out and then remove the comment double-slashes when I'm done with VS Code. In addition to defining the target app from an outdated legacy script execution perspective, it also provides a hopefully "obvious" clue to less knowledgeable end-users as to which app the script is intended to be run in. An alternative would be to use the less common BridgeTalk.appName block.
- I moved the resize to a separate script so I can make base-specific ones. Or would you recommend to always do it in 1 script for specific reasons #eagertolearn 🤓
By @Esther Netherlands
Do what works for you!
You can have one script call another script or have an action call multiple scripts in sequence.
Copy link to clipboard
Copied
Hey @Stephen Marsh Apologies for missing your reply! Thank you so very much for taking the time to explain the need / reason of #target photoshop. Although I am not a real scripter, I learned a lot from you and your codes 🙏🏻 Thanksss!