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

Automating Student Portrait Layouts Using Templates in Photoshop Without Coding

Explorer ,
Mar 11, 2024 Mar 11, 2024
  • Hi!

I'm a professional photographer tasked with taking individual student portraits at various schools each year. After the photo sessions, I usually edit these images in Photoshop, arranging them in an organized manner on a single page. Each student's photo is accompanied by a frame and their name, which I manually add beneath each picture. The names are derived from the photo filenames.I am exploring ways to streamline my workflow by automatically generating this layout using a template. The goal is to place all the individual portraits on one page and automatically add each student's name beneath their photo, utilizing the filenames as the source for the names, all without engaging in complex scripting or coding.Does Photoshop offer any built-in features or perhaps an external plugin that could automate the creation of these layouts from a template? I'm specifically looking for a solution that can auto-populate a template with the photos and their corresponding names, thereby reducing the manual effort involved in the layout and labeling process.I'm open to any advice, guides, or tool recommendations that can help achieve this with minimal manual intervention. If anyone knows of a method to efficiently utilize templates in Photoshop for this purpose, or if there's a more straightforward approach to accomplishing this task, I would greatly appreciate your insights.

Thank you so much for your support and suggestions!

TOPICS
Actions and scripting
3.0K
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
Adobe
Community Expert ,
Mar 11, 2024 Mar 11, 2024
quote

The goal is to place all the individual portraits on one page and automatically add each student's name beneath their photo, utilizing the filenames as the source for the names, all without engaging in complex scripting or coding.


By @Adi1231234

 

This needs scripting, but it doesn't have to be complex. You can use the built-in Contact Sheet II script:

 

contact-sheet.png

The "Filename as Caption" will include the filename extension (.jpg or .psd etc). The default script could be altered or you can create an action using find/replace text to remove the extension and then use Batch to run the action over all open contact sheet documents.

 

P.S. You may also find the following topic of interest:

 

https://community.adobe.com/t5/photoshop-ecosystem-discussions/automate-contact-sheet-ii-is-there-a-...

 

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
Explorer ,
Mar 13, 2024 Mar 13, 2024

Thank you for the suggestion regarding using the Contact Sheet II in Photoshop.

However, what I'm actually looking for is a bit different.

I have a specific design ready that includes gray squares. I want to place a student's photo in each gray square and have the student's name, taken from the file name, appear below the photo.

This layout requires precise placement within predefined templates.

Could you advise on how to achieve this, perhaps with a different tool or method?

Thanks for your help!

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 ,
Mar 13, 2024 Mar 13, 2024

Sorry, I misread your OP and thought that you wanted one student per sheet.

 

Anyway, what you want to do without scripting would leave one last option: Photoshop Variables - AKA, Data Driven Graphics.

 

https://helpx.adobe.com/au/photoshop/using/creating-data-driven-graphics.html

 

Why don't you want to use a script?

 

This sounds similar to the following recent topic using a custom template, but that only used a single image:

 

 
I think that the variables / data driven graphics approach will get you 90% there, then the final PSD output may need adjusting depending on the results of the automated image replacement.
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
Explorer ,
Mar 15, 2024 Mar 15, 2024

Thank you for your valuable insights. I understand now that incorporating scripting might be unavoidable to fully automate my workflow as I envision it.

If you have any script examples or resources that could guide me in automating the process of arranging photos into a template with frames, and automatically adding names from file names, I would greatly appreciate it.

 

I'm exploring the possibility of using Photoshop's frames feature to organize each photo neatly within the montage, aiming for each photo to be automatically aligned and fit into its designated space. This approach is intended to maintain uniformity and enhance the overall professional appearance of the final product.

 

I'm quite new to scripting within Photoshop and would welcome any pointers or examples that specifically address automating image placement, possibly with an emphasis on utilizing frames or similar methods for a clean, organized layout. Thank you once again for your support and assistance.

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 ,
Mar 15, 2024 Mar 15, 2024

@Adi1231234 

 

You could start here:

 

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

 

JJMacksPhotoCollageToolkit > BatchMultiImageCollage.jsx

 

Otherwise, as previously suggested:

 

https://helpx.adobe.com/au/photoshop/using/creating-data-driven-graphics.html

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
Explorer ,
Mar 16, 2024 Mar 16, 2024

Thank you so much for pointing me toward the code examples. I've spent some time reviewing the material, and while it's clear that there's a wealth of information there, I'm finding it a bit challenging to navigate. The examples are quite extensive, with thousands of lines of code, and the variable names and structure are not immediately clear to me. This complexity makes it difficult for me to extract the specific information I need for my project.I was wondering if you could help me find the official Photoshop and JavaScript documentation. I'm looking for a resource that clearly lists every element and its available properties in a way that's easy to understand. While I've come across various guides, I'm in search of something more comprehensive that covers all the basics in a structured format.

 

Do you know where I might find the official documentation, or could you recommend a guide that provides a detailed overview of elements and properties within Photoshop scripting? Such a resource would be incredibly helpful as I continue to refine my project and seek to understand the scripting aspect better.

 

Thank you again for your help and patience; I truly appreciate 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 ,
Mar 16, 2024 Mar 16, 2024

I think that you misunderstood. There is no need to learn scripting in order to make use of these scripts.

 

Download the JJMack script archive that I posted previously – JJMacksPhotoCollageToolkit.

 

Then you will have access to a premade script:

 

BatchMultiImageCollage.jsx

 

BatchMultiImageCollage.png

 

Your template file must follow the four rules:

 

https://web.archive.org/web/20210419210422/http://www.mouseprints.net/old/dpr/PhotoCollageToolkit.ht...

 

  • Size the photo collage templates for the print size you want - width, height and print DPI resolution.
  • Photo collage templates must have a Photoshop background layer. The contents of this layer can be anything.
  • Photo collage templates must have alpha channels named "Image 1", "Image 2", ... "Image n". These map your images Location Position, Shape and size.
  • Photo collage templates layers above the background layers must provide transparent areas to let the images that will be placed below them show through.

 

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
Explorer ,
Mar 16, 2024 Mar 16, 2024

Thank you once again for your support and for directing me towards the BatchMultiImageCollage.jsx script from JJMack's Photo Collage Toolkit. I've explored the capabilities of the script and found it quite innovative in facilitating the automation of the collage creation process. However, there are a few aspects where I'm seeking further solutions, particularly concerning the script's current functionality.

One notable point is while the script adeptly appends the image names beneath their corresponding photos, it unfortunately retains the file extension (e.g., ".jpg") within the displayed names. For the purposes of my project, displaying names without the file extension is crucial. I'm on the lookout for a potential script adjustment or a script feature I might have missed that could exclude the file extension from the names displayed.

Moreover, the process required by the script to create sequentially named alpha channels (e.g., "Image 1", "Image 2", etc.) within the template, while essential, introduces a significant level of complexity and manual preparation that feels somewhat cumbersome for my workflow. Additionally, a pivotal aspect that seems to be missing is the script's capability to handle the fitting of images within frames. The ability to automatically adjust and align images to predefined frame sizes and positions, a key feature when manually dragging images into frames in Photoshop, is not addressed by the script. This absence necessitates a search for either a simplified method or a possible modification of the script that could incorporate this crucial functionality, thereby streamlining the entire template preparation process and minimizing the extensive manual setup currently required before executing the script.

In grappling with these issues, it's becoming clear to me that devising a custom script tailored specifically to meet my unique needs, especially concerning the precise alignment and automatic adjustment of images within frames, may be the most viable path forward. The specific functionality I'm aiming for – akin to the seamless experience of manually dragging and dropping an image into a frame in Photoshop, where the image is auto-fitted to the frame's dimensions and perfectly centered – appears to be beyond the current script's scope. My journey towards finding a more bespoke solution, notwithstanding my sincere appreciation for the utility and potential of the script you've shared, underscores my project's specific demands.

I would be immensely grateful for any additional guidance or support you could extend as I navigate these challenges.

Thank you, once more, for your generous assistance

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 ,
Mar 16, 2024 Mar 16, 2024
quote

I'm on the lookout for a potential script adjustment or a script feature I might have missed that could exclude the file extension from the names displayed.

 

This is trivial with or without scripting.

 

 

quote

Moreover, the process required by the script to create sequentially named alpha channels (e.g., "Image 1", "Image 2", etc.) within the template, while essential, introduces a significant level of complexity and manual preparation that feels somewhat cumbersome for my workflow

 

This only has to happen once, it is indeed a requirement of the template as the alpha channels are used to size and place each image. If you already have frames then it is easy to load them as a selection and create the alpha channel with the appropriate name. I can write a script to rename all alphas from Alpha 1, Alpha 2 etc. to Image 1, Image 2 etc.

 

From memory, the late JJMack didn't like the the place command/smart objects or the frame tool for automation.

 

 

quote

The ability to automatically adjust and align images to predefined frame sizes and positions, a key feature when manually dragging images into frames in Photoshop, is not addressed by the script

 

This is what the alpha channels and layer masks created by the script achieve, just differently.

 

 

quote

In grappling with these issues, it's becoming clear to me that devising a custom script tailored specifically to meet my unique needs, especially concerning the precise alignment and automatic adjustment of images within frames, may be the most viable path forward.

 

That's the issue. You have specific requirements. You either have to adapt your workflow to existing scripts and supplemental tools to achieve the same end or a custom script and or supplemental tools will be required, which isn't trivial.

 

P.S. You haven't commented on the suggestion to look into Photoshop Variables (Data Driven Graphics) which is the other non-scripting approach.

 

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
Enthusiast ,
Mar 14, 2024 Mar 14, 2024

This is what you need https://pixnub.com/

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
Explorer ,
Mar 15, 2024 Mar 15, 2024

Thank you for suggesting the Pixnub website. I took a quick look, and it seems like they offer a variety of tools and plugins that could potentially streamline workflow. Do you have a specific tool or plugin from Pixnub that you would recommend?

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 ,
Mar 14, 2024 Mar 14, 2024

Could you please post screenshots with the pertinent Panels (Toolbar, Layers, Options Bar, …) visible for both the file as it is and the intended result? 

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 ,
Mar 14, 2024 Mar 14, 2024

How are the images’/Layers’ names supposed to be displayed in the montage exactly

I expect file format suffixes should not be displayed, but what about capitalization, spaces/underscores, numbering, …? 

 

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
Explorer ,
Mar 15, 2024 Mar 15, 2024

Hi, thanks for your answer!

The file format suffixes (e.g., .jpg, .png) should not be displayed in the names beneath each portrait. Other than removing the file extension, the names should appear exactly as they are in the filenames, including any capitalization, spaces/underscores, and numbering.

 

Also, I am attaching an example of how I would like it to look (of course, instead of the children's drawings, there will be pictures)

 

Thanks you!

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
LEGEND ,
Mar 14, 2024 Mar 14, 2024

This would probably be easier using InDesign rather than Photoshop.

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 ,
Mar 14, 2024 Mar 14, 2024

Well, it would at least be easier to export multi-page-pdfs from Indesign. 

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
Explorer ,
Mar 15, 2024 Mar 15, 2024

Thank you very much for your suggestion. I did explore the option of using the software you recommended. However, after some consideration, I've decided to continue with Photoshop for this task. I'm more familiar with Photoshop's environment and have access to a broader range of tools that are essential for my workflow beyond this specific task. I appreciate the alternative solution, but I'm keen on finding a way to streamline this process within Photoshop, if possible

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 ,
Mar 16, 2024 Mar 16, 2024

If you create a template with frames, populate it with the images you can use a relatively simple script to add the names as Type Layers. 

Screenshot 2024-03-16 at 12.52.26.pngScreenshot 2024-03-16 at 12.52.37.png

// add type layer with smart objects’ names;
// 2024, use it at your own risk;
if (app.documents.length > 0) {
// set to pixels;
var originalRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
// process;
var theSO = collectSmartObjectsBounds ();
for (var m = 0; m < theSO.length; m++) {
var thisOne = theSO[m];
addTypeLayer (thisOne[0], [thisOne[2][0]+(thisOne[2][2]-thisOne[2][0])/2, thisOne[2][3]+35])
};
// reset;
app.preferences.rulerUnits = originalRulerUnits;
};
////// add type layer //////
function addTypeLayer (theString, theArray) {
var thisLayer = activeDocument.artLayers.add();
thisLayer.kind = LayerKind.TEXT;
thisLayer.name = theString;
var thisLayerRef = thisLayer.textItem;
thisLayerRef.kind = TextType.POINTTEXT;
thisLayerRef.size = 8;
thisLayerRef.font = "Arial-Bold";
var theColor = new SolidColor();
theColor.rgb.red = 0;
theColor.rgb.green = 0;
theColor.rgb.blue = 0;
thisLayerRef.color = theColor;
thisLayerRef.justification = Justification.CENTER;		
thisLayerRef.position = theArray;
thisLayer.blendMode = BlendMode.NORMAL;
thisLayer.opacity = 100;
thisLayer.fillOpacity = 100;
thisLayerRef.useAutoLeading = true;
//thisLayerRef.leading = 0;
thisLayerRef.horizontalScale = 100;
thisLayerRef.verticalScale = 100;
thisLayerRef.contents = theString;
return app.activeDocument.activeLayer
};
////// collect smart objects, probably based on code by paul, mike or x //////
function collectSmartObjectsBounds () {
// the file;
var myDocument = app.activeDocument;
// get number of layers;
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
var applicationDesc = executeActionGet(ref);
var theNumber = applicationDesc.getInteger(stringIDToTypeID("numberOfLayers"));
// process the layers;
var theLayers = new Array;
for (var m = 0; m <= theNumber; m++) {
try {
var ref = new ActionReference();
ref.putIndex( charIDToTypeID( "Lyr " ), m);
var layerDesc = executeActionGet(ref);
var layerSet = typeIDToStringID(layerDesc.getEnumerationValue(stringIDToTypeID("layerSection")));
var isBackground = layerDesc.getBoolean(stringIDToTypeID("background"));
// if not layer group collect values;
if (layerSet != "layerSectionEnd" && layerSet != "layerSectionStart" && isBackground != true) {
var theName = layerDesc.getString(stringIDToTypeID('name'));
var theID = layerDesc.getInteger(stringIDToTypeID('layerID'));
if(layerDesc.hasKey(stringIDToTypeID('smartObject'))) {
var soDesc = layerDesc.getObjectValue(stringIDToTypeID('smartObject'));
var theFileRef = soDesc.getString(stringIDToTypeID('fileReference'));
var theDocID = soDesc.getString(stringIDToTypeID('documentID'));
var isLinked = soDesc.getBoolean(stringIDToTypeID('linked'));
if (isLinked == true) {
var isMissing = soDesc.getBoolean(stringIDToTypeID('linkMissing'));
var isChanged = soDesc.getBoolean(stringIDToTypeID('linkChanged'));
var thePath = soDesc.getPath(stringIDToTypeID('link'));
} else {
var isMissing = undefined;
var isChanged = undefined;
var thePath = undefined
};
/*var x = soDesc.getList(stringIDToTypeID("compsList"));
var theCompsList = soDesc.getObjectValue(stringIDToTypeID("compsList"));
if (theCompsList.count > 2) {
var theCompsList = theCompsList.getList(stringIDToTypeID("compList"));
var theSOComps = new Array;
for (var n = 0; n < theCompsList.count; n++) {
var thisOne = theCompsList.getObjectValue(n);
var compName = thisOne.getString(stringIDToTypeID("name"));
var compID = thisOne.getInteger(stringIDToTypeID("ID"));
var theComment = thisOne.getString(stringIDToTypeID("comment"));
theSOComps.push([compName, compID, theComment]);
};
theLayers.push([theName, theID, theFileRef, theDocID])
theLayers.push([theName, theID, theFileRef, theDocID, theSOComps])
};*/
var theBounds = layerDesc.getObjectValue(stringIDToTypeID("bounds"));
var theseBounds = [theBounds.getUnitDoubleValue(stringIDToTypeID("left")), theBounds.getUnitDoubleValue(stringIDToTypeID("top")), theBounds.getUnitDoubleValue(stringIDToTypeID("right")), theBounds.getUnitDoubleValue(stringIDToTypeID("bottom"))];
var theWidth = theseBounds[2] - theseBounds[0];
var theHeight = theseBounds[3] - theseBounds[1];
var theCenter = [theseBounds[0]+theWidth/2, theseBounds[1]+theHeight/2];
theLayers.push([theName, theID, theseBounds, theWidth, theHeight, theCenter, isLinked, isMissing, isChanged, thePath, theFileRef, theDocID])
}
}
}
catch (e) {};
};
return theLayers
};
////// based on code by mike hale, via paul riggott //////
function selectLayerByID(id,add){ 
add = undefined ? add = false:add 
var ref = new ActionReference();
ref.putIdentifier(charIDToTypeID("Lyr "), id);
var desc = new ActionDescriptor();
desc.putReference(charIDToTypeID("null"), ref );
if(add) desc.putEnumerated( stringIDToTypeID( "selectionModifier" ), stringIDToTypeID( "selectionModifierType" ), stringIDToTypeID( "addToSelection" ) ); 
desc.putBoolean( charIDToTypeID( "MkVs" ), false ); 
try{
executeAction(charIDToTypeID("slct"), desc, DialogModes.NO );
}catch(e){
alert(e.message); 
}
};

 

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
Explorer ,
Mar 16, 2024 Mar 16, 2024

Thank you for sharing your script with me. I appreciate your effort to help streamline my workflow. The script you've provided is interesting and focuses on creating text layers for naming Smart Objects in Photoshop, which is indeed a useful function.

However, my primary goal is slightly different. I am looking for a way to automatically insert individual student portraits into predefined frames within a Photoshop document and then automatically add each student's name, taken from the photo filenames, beneath their respective photo. This process involves both the placement and scaling of images within frames and the generation of text layers for the names.

The challenge I'm facing is how to automate the arrangement of these photos and their corresponding names in a template, to minimize manual work as much as possible. Your script gives me some ideas on handling text layers, but I'm still in need of a solution that can also automatically place and fit the images into the frames.

Do you have any insights, or could you suggest modifications to your script that might address these specific needs? I'm looking for a streamlined approach that combines both image placement and text labeling in an automated fashion.

Thank you again for your contribution and looking forward to any further advice you might have

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 ,
Mar 17, 2024 Mar 17, 2024
quote

Thank you for sharing your script with me. I appreciate your effort to help streamline my workflow. The script you've provided is interesting and focuses on creating text layers for naming Smart Objects in Photoshop, which is indeed a useful function.

However, my primary goal is slightly different. I am looking for a way to automatically insert individual student portraits into predefined frames within a Photoshop document and then automatically add each student's name, taken from the photo filenames, beneath their respective photo. This process involves both the placement and scaling of images within frames and the generation of text layers for the names.

The challenge I'm facing is how to automate the arrangement of these photos and their corresponding names in a template, to minimize manual work as much as possible. Your script gives me some ideas on handling text layers, but I'm still in need of a solution that can also automatically place and fit the images into the frames.

Do you have any insights, or could you suggest modifications to your script that might address these specific needs? I'm looking for a streamlined approach that combines both image placement and text labeling in an automated fashion.

Thank you again for your contribution and looking forward to any further advice you might have


By @Adi1231234

Once you have set up a template with Frames (for example) a Script to populate those with images need not be too complex, depending on what you want to achieve.

Screenshot 2024-03-17 at 16.59.27.pngScreenshot 2024-03-17 at 17.01.53.png 

// fill frames in duplicate/s of active document with selected images;
// 2024, use it at your own risk;
if (app.documents.length > 0) {
var theFiles = selectFile (true);
var myDocument = activeDocument;
var theCount = 0;
var thePages = 1;
var theCopy = myDocument.duplicate("file"+thePages, false);
var theFrames =  collectFrames ();
for (var m = theFiles.length-1; m >= 0; m--) {
    fillFrame (theFrames[theCount][2], theFiles[m]);
    theCount++;
    if (theCount == theFrames.length) {
        theCount = 0;
        thePages++;
        activeDocument = myDocument;
        var theCopy = myDocument.duplicate("file"+thePages, false);
        var theFrames =  collectFrames ();
    }
};
};
////// frame //////
function fillFrame (theFrame, theFile) {
selectLayerByID(theFrame,false);
var idpixelsUnit = stringIDToTypeID( "pixelsUnit" );
var idplaceEvent = stringIDToTypeID( "placeEvent" );
var idlayer = stringIDToTypeID( "layer" );
var idoffset = stringIDToTypeID( "offset" );
var desc7 = new ActionDescriptor();
    desc7.putInteger( stringIDToTypeID( "ID" ), theFrame );
    desc7.putPath( stringIDToTypeID( "null" ), new File( theFile ) );
    desc7.putEnumerated( stringIDToTypeID( "freeTransformCenterState" ), stringIDToTypeID( "quadCenterState" ), stringIDToTypeID( "QCSAverage" ) );
        var desc8 = new ActionDescriptor();
        desc8.putUnitDouble( stringIDToTypeID( "horizontal" ), idpixelsUnit, 0.000000 );
        desc8.putUnitDouble( stringIDToTypeID( "vertical" ), idpixelsUnit, 0.000000 );
    desc7.putObject( idoffset, idoffset, desc8 );
        /*var desc9 = new ActionDescriptor();
            var ref1 = new ActionReference();
            ref1.putIdentifier( idlayer, 4 );
        desc9.putReference( stringIDToTypeID( "from" ), ref1 );
            var ref2 = new ActionReference();
            ref2.putIdentifier( idlayer, 34 );
        desc9.putReference( stringIDToTypeID( "to" ), ref2 );
    desc7.putObject( stringIDToTypeID( "replaceLayer" ), idplaceEvent, desc9 );*/
executeAction( idplaceEvent, desc7, DialogModes.NO );
};
////// 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, '*.jpg;*.tif;*.psd;*.png', multi)}
else {var theFiles = File.openDialog (theString, getFiles, multi)};
////// filter files  for mac //////
function getFiles (theFile) {
if (theFile.name.match(/\.(jpg|tif|psd|png)$/i) || theFile.constructor.name == "Folder") {
return true
};
};
return theFiles
};
////// collect layers //////
function collectFrames () {
// get number of layers;
var ref = new ActionReference();
ref.putProperty(stringIDToTypeID('property'), stringIDToTypeID('numberOfLayers'));
ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
var applicationDesc = executeActionGet(ref);
var theNumber = applicationDesc.getInteger(stringIDToTypeID("numberOfLayers"));
// process the layers;
var theLayers = new Array;
for (var m = 0; m <= theNumber; m++) {
try {
var ref = new ActionReference();
ref.putIndex( charIDToTypeID( "Lyr " ), m);
var layerDesc = executeActionGet(ref);
//checkDesc2 (layerDesc, true);
var layerSet = typeIDToStringID(layerDesc.getEnumerationValue(stringIDToTypeID("layerSection")));
var isBackground = layerDesc.getBoolean(stringIDToTypeID("background"));
// if group collect values;
if (layerSet != "layerSectionEnd" /*&& layerSet != "layerSectionStart" && isBackground != true*/) {
var theName = layerDesc.getString(stringIDToTypeID('name'));
var hasFrame = layerDesc.hasKey(stringIDToTypeID("framedGroup"));
var theID = layerDesc.getInteger(stringIDToTypeID('layerID'));
var theIndex = layerDesc.getInteger(stringIDToTypeID('itemIndex'));
var theColor = typeIDToStringID(layerDesc.getEnumerationValue(stringIDToTypeID("color")));
if (layerSet == "layerSectionStart" && hasFrame == true) {
theLayers.push([theName, theIndex, theID, theColor])
};
};
}
catch (e) {};
};
return theLayers
};
////// based on code by michael l hale //////
function checkDesc2 (theDesc, theAlert) {
var c = theDesc.count;
var str = '';
for(var i=0;i<c;i++){ //enumerate descriptor's keys
str = str + 'Key '+i+' = '+typeIDToStringID(theDesc.getKey(i))+': '+theDesc.getType(theDesc.getKey(i))+'\n'+getValues (theDesc, i)+'\n';
};
if (theAlert == true) {alert("desc\n\n"+str);
return};
return str
};
////// check //////
function getValues (theDesc, theNumber) {
switch (theDesc.getType(theDesc.getKey(theNumber))) {
case DescValueType.ALIASTYPE:
return theDesc.getPath(theDesc.getKey(theNumber));
break;
case DescValueType.BOOLEANTYPE:
return theDesc.getBoolean(theDesc.getKey(theNumber));
break;
case DescValueType.CLASSTYPE:
return theDesc.getClass(theDesc.getKey(theNumber));
break;
case DescValueType.DOUBLETYPE:
return theDesc.getDouble(theDesc.getKey(theNumber));
break;
case DescValueType.ENUMERATEDTYPE:
return (typeIDToStringID(theDesc.getEnumerationValue(theDesc.getKey(theNumber)))+"_"+typeIDToStringID(theDesc.getEnumerationType(theDesc.getKey(theNumber))));
break;
case DescValueType.INTEGERTYPE:
return theDesc.getInteger(theDesc.getKey(theNumber));
break;
case DescValueType.LISTTYPE:
return theDesc.getList(theDesc.getKey(theNumber));
break;
case DescValueType.OBJECTTYPE:
return (theDesc.getObjectValue(theDesc.getKey(theNumber))+"_"+typeIDToStringID(theDesc.getObjectType(theDesc.getKey(theNumber))));
break;
case DescValueType.RAWTYPE:
return theDesc.getReference(theDesc.getData(theNumber));
break;
case DescValueType.REFERENCETYPE:
return theDesc.getReference(theDesc.getKey(theNumber));
break;
case DescValueType.STRINGTYPE:
return theDesc.getString(theDesc.getKey(theNumber));
break;
case DescValueType.UNITDOUBLE:
return (theDesc.getUnitDoubleValue(theDesc.getKey(theNumber))+"_"+typeIDToStringID(theDesc.getUnitDoubleType(theDesc.getKey(theNumber))));
break;
default: 
break;
};
};
////// based on code by mike hale, via paul riggott //////
function selectLayerByID(id,add){ 
	add = undefined ? add = false:add 
	var ref = new ActionReference();
		ref.putIdentifier(charIDToTypeID("Lyr "), id);
		var desc = new ActionDescriptor();
		desc.putReference(charIDToTypeID("null"), ref );
		   if(add) desc.putEnumerated( stringIDToTypeID( "selectionModifier" ), stringIDToTypeID( "selectionModifierType" ), stringIDToTypeID( "addToSelection" ) ); 
		  desc.putBoolean( charIDToTypeID( "MkVs" ), false ); 
	   try{
		executeAction(charIDToTypeID("slct"), desc, DialogModes.NO );
	}catch(e){
	alert(e.message); 
	}
};  




////// duplicate layer and move, rotate and scale it //////
function duplicateMoveRotateScale (theID, theX, theY, theScaleX, theScaleY, theRotation, theSkew, theDuplicate) {
selectLayerByID(theID,false);
try{
var idTrnf = charIDToTypeID( "Trnf" );
    var desc10 = new ActionDescriptor();
    var idnull = charIDToTypeID( "null" );
        var ref6 = new ActionReference();
        ref6.putIdentifier( charIDToTypeID( "Lyr " ), theID );
        ref6.putEnumerated( charIDToTypeID( "Lyr " ), charIDToTypeID( "Ordn" ), charIDToTypeID( "Trgt" ) );
    desc10.putReference( idnull, ref6 );
    var idFTcs = charIDToTypeID( "FTcs" );
    var idQCSt = charIDToTypeID( "QCSt" );
    var idQcsa = charIDToTypeID( "Qcsa" );
    desc10.putEnumerated( idFTcs, idQCSt, idQcsa );
    var idOfst = charIDToTypeID( "Ofst" );
        var desc11 = new ActionDescriptor();
        var idHrzn = charIDToTypeID( "Hrzn" );
        var idPxl = charIDToTypeID( "#Pxl" );
        desc11.putUnitDouble( idHrzn, idPxl, theX );
        var idVrtc = charIDToTypeID( "Vrtc" );
        var idPxl = charIDToTypeID( "#Pxl" );
        desc11.putUnitDouble( idVrtc, idPxl, theY );
    var idOfst = charIDToTypeID( "Ofst" );
    desc10.putObject( idOfst, idOfst, desc11 );
    var idWdth = charIDToTypeID( "Wdth" );
    var idPrc = charIDToTypeID( "#Prc" );
    desc10.putUnitDouble( idWdth, idPrc, theScaleX );
    var idHght = charIDToTypeID( "Hght" );
    var idPrc = charIDToTypeID( "#Prc" );
    desc10.putUnitDouble( idHght, idPrc, theScaleY );
    var idAngl = charIDToTypeID( "Angl" );
    var idAng = charIDToTypeID( "#Ang" );
    desc10.putUnitDouble( idAngl, idAng, theRotation );

    var idskew = stringIDToTypeID( "skew" );
        var desc358 = new ActionDescriptor();
        var idhorizontal = stringIDToTypeID( "horizontal" );
        var idangleUnit = stringIDToTypeID( "angleUnit" );
        desc358.putUnitDouble( idhorizontal, idangleUnit, theSkew );
        var idvertical = stringIDToTypeID( "vertical" );
        var idangleUnit = stringIDToTypeID( "angleUnit" );
        desc358.putUnitDouble( idvertical, idangleUnit, 0.000000 );
    var idpaint = stringIDToTypeID( "paint" );
    desc10.putObject( idskew, idpaint, desc358 );

    var idIntr = charIDToTypeID( "Intr" );
    var idIntp = charIDToTypeID( "Intp" );
    var idbicubicAutomatic = stringIDToTypeID( "bicubicAutomatic" );
    desc10.putEnumerated( idIntr, idIntp, idbicubicAutomatic );
    var idCpy = charIDToTypeID( "Cpy " );
    desc10.putBoolean( idCpy, theDuplicate );
executeAction( idTrnf, desc10, DialogModes.NO );
} catch (e) {}
};
////// get active layer’s id //////
function getLayerId () {
    var ref = new ActionReference();
    ref.putProperty (stringIDToTypeID ("property"), stringIDToTypeID ("layerID"));
    ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
    return executeActionGet(ref).getInteger(stringIDToTypeID("layerID"));
};

 

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
Explorer ,
Mar 23, 2024 Mar 23, 2024

 

Thank you very much for the time and effort you put into crafting such a detailed and thoughtful response, along with the code you shared! I truly appreciate the help and guidance you've provided. Interestingly, I've also been working on a similar solution to the problem I posted about and have developed a piece of code that accomplishes the same goal. I'm thrilled to see how our approaches compare and contrast, and I believe there's a lot we can learn from each other's methodologies. Below is the code I've worked on, and I'm looking forward to discussing our solutions further and exploring ways we might improve or refine them by combining our ideas. Thanks again for your invaluable contribution to the discussion!

 

try {
    var totalTime = 0;
    var startTime = Date.now();
    var doc = app.activeDocument;

    run();

    var endTime = Date.now();
    totalTime += endTime - startTime;
    $.writeln("Total execution time: " + totalTime + "ms");;

} catch (e) {
    $.writeln("failed ", e);;
}

function run() {
    const frames = getAllEmptyFrames();
    const textBoxes = getAllTextBoxs();
    var selectedFiles = openDialogMultipleImages();
    placeImagesInFrames(frames, textBoxes, selectedFiles);
}

function placeImagesInFrames(frames, textBoxes, files) {
    var iterates = Math.min(frames.length, files.length)
    for (var i = 0; i < iterates; i++) {
        var frame = frames[i];
        var file = files[i];
        placeInFrame(frame, file);

        var textLayer = findBottomClosestLayer(frame, textBoxes);

        if (textLayer) {
            textLayer.textItem.contents = getFileName(file);
        } 
    }
}



////// frame ////// - modified from a script by Stephen_A_Marsh!

function placeInFrame(frameLayer, file) {
    app.activeDocument.activeLayer = frameLayer;

    function s2t(s) {
        return app.stringIDToTypeID(s);
    }
    var descriptor = new ActionDescriptor();
    var nullId = s2t("null");
    descriptor.putPath(nullId, file); // File path

    var linkedId = s2t("linked");
    descriptor.putBoolean(linkedId, false); // Linked/embedded boolean

    var freeTransformCenterStateId = s2t("freeTransformCenterState");
    var quadCenterStateId = s2t("quadCenterState");
    var QCSAverageId = s2t("QCSAverage");
    descriptor.putEnumerated(freeTransformCenterStateId, quadCenterStateId, QCSAverageId); // Place and fit

    var placeEventId = s2t("placeEvent");
    executeAction(placeEventId, descriptor, DialogModes.NO);
}

function isFrame(layer) {
    try {
        doc.activeLayer = layer;
        var r = new ActionReference();
        r.putEnumerated(stringIDToTypeID('layer'), stringIDToTypeID('ordinal'), stringIDToTypeID('targetEnum'));
        var options = executeActionGet(r);
        return options.hasKey(stringIDToTypeID('framedGroup'));
    } catch (e) {}
}

function getAllEmptyFrames() {
    var layers = doc.layers;
    var emptyFrames = [];
    for (var i = 0; i < layers.length; i++) {
        var layer = layers[i];
        if (!isFrame(layer) && layer.visible && layer.artLayers && !layer.artLayers.length) {
            emptyFrames.push(layer);
        }
    }
    return emptyFrames;
}



////// textBox //////

function getAllTextBoxs() {
    var layers = doc.layers;
    var textBoxs = [];
    for (var i = 0; i < layers.length; i++) {
        var layer = layers[i];
        if (layer.kind == LayerKind.TEXT) {
            textBoxs.push(layer);
        }
    }
    return textBoxs;
}



////// distance //////

function findBottomClosestLayer(layer1, array2) {
    var closestLayer = null;
    var minDistance = Number.MAX_VALUE;
    var layer1Center = calculateCenter(layer1.bounds);

    for (var j = 0; j < array2.length; j++) {
        var layer2 = array2[j];
        var layer2Center = calculateCenter(layer2.bounds);

        if (layer2Center.y > layer1Center.y) {
            var distance = euclideanDistance(layer1Center, layer2Center);
            if (distance < minDistance) {
                minDistance = distance;
                closestLayer = layer2;
            }
        }
    }

    return closestLayer;
}

function calculateCenter(bounds) {
    var x = (bounds[0] + bounds[2]) / 2;
    var y = (bounds[1] + bounds[3]) / 2;
    return {
        x: x,
        y: y
    };
}

function euclideanDistance(point1, point2) {
    var dx = point2.x - point1.x;
    var dy = point2.y - point1.y;
    return Math.sqrt(dx * dx + dy * dy);
}



////// file //////

function getFileName(file) {
    return file.name.split('.').slice(0, -1).join('.')
}

function openDialogMultipleImages(file) {
    return File.openDialog("Select the files to place:", "Select:*.nef;*.cr3;*.cr2;*.crw;*.dcs;*.raf;*.arw;*.orf;*.dng;*.psd;*.tif;*.tiff;*.jpg;*.jpe;*.jpeg;*.png;*.bmp", true);
}

 

Feel free to review my approach and share any feedback or suggestions you might have.

 

Also, I want to take a moment to extend my gratitude to Stephen. Your insights have been incredibly helpful not just here but also in another discussion where your expertise really shone through. For those interested, here's a link to that other question where Stephen's assistance was invaluable: https://community.adobe.com/t5/photoshop-ecosystem-discussions/photoshop-scripting-image-placement-w...

Your willingness to share your knowledge and help others is truly appreciated.
Thank you, @Stephen Marsh!

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
Explorer ,
Apr 19, 2024 Apr 19, 2024
LATEST

Hey,

UPDATE: We have identified inaccuracies in the function getAllEmptyFrames. Thanks to the insightful contributions from @Stephen Marsh  we have successfully revised it. Below is the revised logic that supersedes the previous iteration. Please be aware that this modification will extend the script’s operation to all frames, rather than limiting it to only the empty ones.

 

function isCurrentLayerFrame() {
    try {
        var r = new ActionReference();
        r.putEnumerated(stringIDToTypeID('layer'), stringIDToTypeID('ordinal'), stringIDToTypeID('targetEnum'));
        var options = executeActionGet(r);
        return options.hasKey(stringIDToTypeID('framedGroup')); // Test for the 'framedGroup' key
    } catch (e) {
        return false;
    }
}

function isFrame(layer) {
    app.activeDocument.activeLayer = layer;

    if (layer.kind == LayerKind.SMARTOBJECT) {
        app.activeDocument.activeLayer = layer.parent;
        if (layer.parent.typename === "LayerSet" && isCurrentLayerFrame() === true) {
            return true;
        }
    } else if (layer.typename === "LayerSet" && isCurrentLayerFrame() === true) {
        return true;
    }
    return false;
}


function getAllFrames(layers) {
    var emptyFrames = [];
    for (var i = 0; i < layers.length; i++) {
        var layer = layers[i];
        var isLayerFrame = isFrame(layer);

        if (isLayerFrame) {
            emptyFrames.push(layer);
        }
    }
    return emptyFrames;
}

 

The conversation that led to this result: https://community.adobe.com/t5/photoshop-ecosystem-discussions/photoshop-scripting-image-placement-w...

 

Additionally, it is important to note that the full script did not function in Photoshop 2020, but performed flawlessly in the 2022 version.

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 ,
Mar 18, 2024 Mar 18, 2024

@Adi1231234 

 

Have you tried the latest script from @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