Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
0

How to write JSX script to embed other psd into current document

Community Beginner ,
May 13, 2025 May 13, 2025

Hello all,

 

Developer here, trying to create a script to help me create a large photoshop document. I have a list of file names, and the x/y positions (I can change this input in any way to make it easier for PS to read). All of the file names are an existing .psd file.

 

If I were to do this manually, I would do:
1. File > Place embedded... > Select the file from my file browser > Ok

2. Set the X and Y coordinates for the newly embeded object

(however, I'd need to do this over 400 times manually, thus the script)

 

I have tried my absolute best to navigate the PDF documentation (wtf Adobe), but I can't even figure out how to embed an existing .pdf file into the current document from a file path (I've seen some examples of using a filepicker, but that doesn't work). Without even getting past that I don't know how to place it via the X/Y coords.

 

Any help would be greatly appreciated! Please let me know if there's any more information I can provide to help.

TOPICS
Actions and scripting , macOS
1.9K
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 2 Correct answers

Community Expert , May 17, 2025 May 17, 2025

Screenshot 2025-05-17 at 12.55.49.png

This would raise a file-selection-dialog for a txt-file along the lines of the example you posted, then place the images. 

// 2025, use it at your own risk;
if (app.documents.length > 0) {
var originalRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
app.runMenuItem(charIDToTypeID('FtOn'));
var theFile = selectFile (false);
var theFiles = readText (theFile);
var myDocument = activeDocument;
var thePath = myDocument.path;
var docWidth = myDocument.width;
var doc
...
Translate
Community Beginner , May 19, 2025 May 19, 2025

@c.pfaffenbichler @Stephen Marsh It works! Thank you SO MUCH! I had to make a few small adjustments, and added some comments for clairity - here's what I ended up with:

/*
 * Create your base file (this script may be set to expect inchees and 300px/inch for your base file)
 * All images should be in .psd format
 * Add your images to a subfolder in the same directory as your base file
 * Enter the subfolder name from above into the var on line 27 (var subFolder)
 * The input should be a .txt file 
...
Translate
Adobe
Community Expert ,
May 13, 2025 May 13, 2025

For legacy ExtendScript, you can take a look at the following as an example for place embedded or linked:

 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
May 14, 2025 May 14, 2025

Thanks @Stephen Marsh ! The example you sent for embedding the images looks like I should be able to modify it slightly for my use case.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
May 14, 2025 May 14, 2025

Hi @Stephen Marsh - thanks again for your example. I tried modifying it to what I think it needs, but I have no idea how to test/debug it. I added comments where I made changes or had questions. I'm going to keep looking to see if I can find any sort of VSCode extension or instructions on how to debug the code (if you have any suggestions I'd love them!). If you have any thoughts or suggestions I would also really appreciate it. I think I tried to set it up that the base document that I want the psd files to be placed on is the only document currently open when the script is run.

 

/*
Stacker - Place Embedded.jsx
Stephen Marsh, v1.0
https://community.adobe.com/t5/photoshop-ecosystem-discussions/layers-creating-layers/td-p/13252109
*/

#target photoshop

const filesToPlace = [
   { name: "Head 1", x: 0, y: 0 },
   { name: "Head 2", x: 6, y: 0 },
   { name: "Head 3", x: 12, y: 0 },
   { name: "Head 3", x: -3, y: 3.5 },
   { name: "Head 1", x: 3, y: 3.5 },
   { name: "Head 2", x: 9, y: 3.5 }
];

// I want the currently open document to be the base document.
// It's an empty psd doc with defined width and height in inches, with a background layer of white.
if (app.documents.length === 1) {
    (function () {
        var savedDisplayDialogs = app.displayDialogs;
        app.displayDialogs = DialogModes.NO;
        var origUnits = app.preferences.rulerUnits;
        // Changed to Inches
        app.preferences.rulerUnits = Units.INCHES;

        // I put all my source images in a folder for this to work
        var inputFolder = Folder.selectDialog('Please select the input folder:');
        if (inputFolder === null) {
            app.beep();
            return;
        }

        var inputFiles = inputFolder.getFiles(/\.psd$/i);
        

        // This is duplicated from above? Necessary?
        // app.displayDialogs = DialogModes.NO;

        // I want the currently open document to be the base document.
        // var baseDoc = open(inputFiles[0]);
        var baseDoc = activeDocument;
        // Do I need to duplicate anything here?
        baseDoc.duplicate("Stacker", false);
        // Why am I closing the base document?
        baseDoc.close(SaveOptions.DONOTSAVECHANGES);

        for (let fileToPlace of filesToPlace) {
            // inputFiles has the list of all possible files (about 13)
            // filesToPlace is the array of files that I want to place with their coords (about 240 entries)
            // (Imagine 13 pics being used to create a collage)
            // As we loop through filesToPlace, we want to find the file in inputFiles that matches the name
            const file = inputFiles.find(f => f.name === fileToPlace.name);
            placeFile(new File(file), false, fileToPlace.x, fileToPlace.y);
            activeDocument.activeLayer.name = fileToPlace.name;
        }

        // Is it necessary to remove the background layer?
        activeDocument.activeLayer = activeDocument.backgroundLayer;
        activeDocument.activeLayer.remove();

        app.beep();
        alert(inputFiles.length + ' files placed!');

        // Restore original settings
        app.displayDialogs = savedDisplayDialogs;
        app.preferences.rulerUnits = origUnits;


        // Functions
        function placeFile(null2, linked, horizontal, vertical) {
            var s2t = function (s) {
                return app.stringIDToTypeID(s);
            };
            var AD = new ActionDescriptor();
            AD.putInteger(s2t("ID"), 1);
            AD.putPath(s2t("null"), null2);
            AD.putBoolean(s2t("linked"), linked); // false for embedded
            AD.putEnumerated(s2t("freeTransformCenterState"), s2t("quadCenterState"), s2t("QCSAverage"));
            // Is "inchesUnit" the correct unit?
            AD.putUnitDouble(s2t("horizontal"), s2t("inchesUnit"), horizontal);
            AD.putUnitDouble(s2t("vertical"), s2t("inchesUnit"), vertical);
            AD.putObject(s2t("offset"), s2t("offset"), AD);
            executeAction(s2t("placeEvent"), AD, DialogModes.NO);
        }
    })();
} else {
    alert('Please only have the base file open before running this script...');
}
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
May 15, 2025 May 15, 2025

There is an ExtendScript Debugger for Visual Studio Code:

 

https://marketplace.visualstudio.com/items?itemName=Adobe.extendscript-debug

 

AFAIK, you should only use var, not let or const for ES (otherwise look into the new, modern UXP scripting).

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
May 14, 2025 May 14, 2025

What file formats?

What do the coordinates describe – center, top left, …? 

 

Please post the receiving file, a few sample files to place and the corresponding list. 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
May 14, 2025 May 14, 2025

Hi @c.pfaffenbichler ,

 

Sure. Here's some sample files (all .psd). The coordinates describe the X/Y coordinates (all units in the attached file and my comments are in inches - I can change that if needed). Sample input would look like:
Head 1, X: 0, Y: 0

Head 2, X: 6, Y: 0

Head 3, X: 12, Y: 0

...

Head 3, X: -3, Y: 3.5

Head 1, X: 3, Y: 3.5

Head 2, X: 9, Y: 3.5

...

There are 23 rows, with 10/11 images per row. Even rows have 10 images and X starts at 0. Odd rows have 11 images and X starts at -3 (yes, the image will be clipped at the beginning and end of the row). If you need the full data, I can provide that as well (If the application is in .jsx, I plan on just pasting the data as an variable at the top of the file - no need to make it reusable by uploading a .csv of this data and parsing it).

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
May 17, 2025 May 17, 2025

Screenshot 2025-05-17 at 12.55.49.png

This would raise a file-selection-dialog for a txt-file along the lines of the example you posted, then place the images. 

// 2025, use it at your own risk;
if (app.documents.length > 0) {
var originalRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
app.runMenuItem(charIDToTypeID('FtOn'));
var theFile = selectFile (false);
var theFiles = readText (theFile);
var myDocument = activeDocument;
var thePath = myDocument.path;
var docWidth = myDocument.width;
var docHeight = myDocument.height;
// place images;
var thisOne = theFiles[0];
placeScaleRotateFile (thePath+"/"+thisOne[0]+".psd", docWidth/(-2) + (thisOne[1]*300),  docHeight/(-2) + (thisOne[2]*300), 100, 100, 0, false);
// assumes the images have the same dimensions;
var xOffset = myDocument.activeLayer.bounds[0]*(-1);
var yOffset = myDocument.activeLayer.bounds[1]*(-1);
myDocument.activeLayer.translate(xOffset, yOffset);
for (var m = 1; m < theFiles.length; m++) {
var thisOne = theFiles[m];
placeScaleRotateFile (thePath+"/"+thisOne[0]+".psd", docWidth/(-2) + (thisOne[1]*300) + xOffset,  docHeight/(-2) + (thisOne[2]*300) + yOffset, 100, 100, 0, false)
};
// reset;
app.preferences.rulerUnits = originalRulerUnits;
};
////////////////////////////////////
////// place //////
function placeScaleRotateFile (file, xOffset, yOffset, theXScale, theYScale, theAngle, linked) {
var idPxl = charIDToTypeID( "#Pxl" );
var idPrc = charIDToTypeID( "#Prc" );
var idPlc = charIDToTypeID( "Plc " );
var desc5 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
desc5.putPath( idnull, new File( file ) );
var idFTcs = charIDToTypeID( "FTcs" );
var idQCSt = charIDToTypeID( "QCSt" );
var idQcsa = charIDToTypeID( "Qcsa" );
desc5.putEnumerated( idFTcs, idQCSt, idQcsa );
var idOfst = charIDToTypeID( "Ofst" );
var desc6 = new ActionDescriptor();
var idHrzn = charIDToTypeID( "Hrzn" );
desc6.putUnitDouble( idHrzn, idPxl, xOffset );
var idVrtc = charIDToTypeID( "Vrtc" );
desc6.putUnitDouble( idVrtc, idPxl, yOffset );
var idOfst = charIDToTypeID( "Ofst" );
desc5.putObject( idOfst, idOfst, desc6 );
var idWdth = charIDToTypeID( "Wdth" );
desc5.putUnitDouble( idWdth, idPrc, theYScale );
var idHght = charIDToTypeID( "Hght" );
desc5.putUnitDouble( idHght, idPrc, theXScale );
var idAngl = charIDToTypeID( "Angl" );
var idAng = charIDToTypeID( "#Ang" );
desc5.putUnitDouble( idAngl, idAng,theAngle );	
if (linked == true) {
var idLnkd = charIDToTypeID( "Lnkd" );
desc5.putBoolean( idLnkd, true );
};
executeAction( idPlc, desc5, DialogModes.NO );
// get layerid;
var ref = new ActionReference();
ref.putProperty (stringIDToTypeID ("property"), stringIDToTypeID ("layerID"));
ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
var layerDesc = executeActionGet(ref);
var layerID = layerDesc.getInteger (stringIDToTypeID ("layerID"));
return [app.activeDocument.activeLayer, layerID];
};
////// read prefs file //////
function readText (thePath) {
if (File(thePath).exists == true) {
var file = File(thePath);
file.open("r");
file.encoding= 'BINARY';
var theText = new String;
for (var m = 0; m < file.length; m ++) {
theText = theText.concat(file.readch());
};
var theArray = theText.split("\n");
for (var n = 0; n < theArray.length; n++) {
theArray[n] = theArray[n].replace("X: ", "");
theArray[n] = theArray[n].replace("Y: ", "")
theArray[n] = theArray[n].split(", ");
};
file.close();
return theArray
}
};
////// select file //////
function selectFile (multi) {
if (multi == true) {var theString = "please select files"}
else {var theString = "please select one file"};
if ($.os.search(/windows/i) != -1) {var theFiles = File.openDialog (theString, '*.txt', multi)}
else {var theFiles = File.openDialog (theString, getFiles, multi)};
////// filter files  for mac //////
function getFiles (theFile) {
if (theFile.name.match(/\.(txt)$/i) || theFile.constructor.name == "Folder") {
return true
};
};
return theFiles
};

 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
May 19, 2025 May 19, 2025

@elliotstoner – How did you go with the script offered by @c.pfaffenbichler ?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
May 19, 2025 May 19, 2025
LATEST

@c.pfaffenbichler @Stephen Marsh It works! Thank you SO MUCH! I had to make a few small adjustments, and added some comments for clairity - here's what I ended up with:

/*
 * Create your base file (this script may be set to expect inchees and 300px/inch for your base file)
 * All images should be in .psd format
 * Add your images to a subfolder in the same directory as your base file
 * Enter the subfolder name from above into the var on line 27 (var subFolder)
 * The input should be a .txt file with the following format:
 *    imageName,xOffset,yOffset (each on a separate line)
 *    example: Image 1,-3,4.5
 */

if (app.documents.length > 0) {
  // Save prefs
  var originalRulerUnits = app.preferences.rulerUnits;

  // Setup
  app.preferences.rulerUnits = Units.PIXELS;
  app.runMenuItem(charIDToTypeID("FtOn"));
  var theFile = selectFile(false);
  var theFiles = readText(theFile);
  var myDocument = activeDocument;
  var thePath = myDocument.path;
  var docWidth = myDocument.width;
  var docHeight = myDocument.height;

  // The root directory is the location of the base file - put all of your images in a
  // subfolder in the same directory as your base file, and put the name of that folder here.
  var subFolder = "6in width";

  // Place images
  var thisOne = theFiles[0];
  placeScaleRotateFile(
    thePath + "/" + subFolder + "/" + thisOne[0] + ".psd",
    docWidth / -2 + thisOne[1] * 300,
    docHeight / -2 + thisOne[2] * 300,
    100,
    100,
    0,
    false
  );

  // assumes the images have the same dimensions;
  var xOffset = myDocument.activeLayer.bounds[0] * -1;
  var yOffset = myDocument.activeLayer.bounds[1] * -1;
  myDocument.activeLayer.translate(xOffset, yOffset);
  for (var m = 1; m < theFiles.length; m++) {
    var thisOne = theFiles[m];
    placeScaleRotateFile(
      thePath + "/" + subFolder + "/" + thisOne[0] + ".psd",
      docWidth / -2 + thisOne[1] * 300 + xOffset,
      docHeight / -2 + thisOne[2] * 300 + yOffset,
      100,
      100,
      0,
      false
    );
  }

  // Reset prefs
  app.preferences.rulerUnits = originalRulerUnits;
}

// ------- Helper Functions -------
// Place File, Scale, and Rotate File
function placeScaleRotateFile(
  file,
  xOffset,
  yOffset,
  theXScale,
  theYScale,
  theAngle,
  linked
) {
  var idPxl = charIDToTypeID("#Pxl");
  var idPrc = charIDToTypeID("#Prc");
  var idPlc = charIDToTypeID("Plc ");
  var desc5 = new ActionDescriptor();
  var idnull = charIDToTypeID("null");
  desc5.putPath(idnull, new File(file));
  var idFTcs = charIDToTypeID("FTcs");
  var idQCSt = charIDToTypeID("QCSt");
  var idQcsa = charIDToTypeID("Qcsa");
  desc5.putEnumerated(idFTcs, idQCSt, idQcsa);
  var idOfst = charIDToTypeID("Ofst");
  var desc6 = new ActionDescriptor();
  var idHrzn = charIDToTypeID("Hrzn");
  desc6.putUnitDouble(idHrzn, idPxl, xOffset);
  var idVrtc = charIDToTypeID("Vrtc");
  desc6.putUnitDouble(idVrtc, idPxl, yOffset);
  var idOfst = charIDToTypeID("Ofst");
  desc5.putObject(idOfst, idOfst, desc6);
  var idWdth = charIDToTypeID("Wdth");
  desc5.putUnitDouble(idWdth, idPrc, theYScale);
  var idHght = charIDToTypeID("Hght");
  desc5.putUnitDouble(idHght, idPrc, theXScale);
  var idAngl = charIDToTypeID("Angl");
  var idAng = charIDToTypeID("#Ang");
  desc5.putUnitDouble(idAngl, idAng, theAngle);
  if (linked == true) {
    var idLnkd = charIDToTypeID("Lnkd");
    desc5.putBoolean(idLnkd, true);
  }
  executeAction(idPlc, desc5, DialogModes.NO);
  // get layerid;
  var ref = new ActionReference();
  ref.putProperty(stringIDToTypeID("property"), stringIDToTypeID("layerID"));
  ref.putEnumerated(
    charIDToTypeID("Lyr "),
    charIDToTypeID("Ordn"),
    charIDToTypeID("Trgt")
  );
  var layerDesc = executeActionGet(ref);
  var layerID = layerDesc.getInteger(stringIDToTypeID("layerID"));
  return [app.activeDocument.activeLayer, layerID];
}

// ------- Read input file ------
function readText(thePath) {
  if (File(thePath).exists == true) {
    var file = File(thePath);
    file.open("r");
    file.encoding = "BINARY";
    var theText = new String();
    for (var m = 0; m < file.length; m++) {
      theText = theText.concat(file.readch());
    }
    var theArray = theText.split("\n");
    for (var n = 0; n < theArray.length; n++) {
      theArray[n] = theArray[n].split(",");
    }
    file.close();
    return theArray;
  }
}

// ------- Select input file ------
function selectFile(multi) {
  if (multi == true) {
    var theString = "please select files";
  } else {
    var theString = "please select one file";
  }
  if ($.os.search(/windows/i) != -1) {
    var theFiles = File.openDialog(theString, "*.txt", multi);
  } else {
    var theFiles = File.openDialog(theString, getFiles, multi);
  }
  // Filter files for Mac
  function getFiles(theFile) {
    if (
      theFile.name.match(/\.(txt)$/i) ||
      theFile.constructor.name == "Folder"
    ) {
      return true;
    }
  }
  return theFiles;
}
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
May 15, 2025 May 15, 2025

@elliotstoner 

 

Another option is to use the late JJMack's BatchMultiImageCollage.jsx script, where all you need to do is create an alpha channel in the required sizes and positions in your template background (named Image 1, Image 2 etc).

 

I have attached a PSD (I had to convert it to grayscale to fit the upload limit).

 

https://github.com/MarshySwamp/JJMack-Archive

PhotoCollageToolkit.zip

 

EDIT: Forget that idea, I just realised that you're going to run out of alpha channels.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines