Skip to main content
Participating Frequently
March 17, 2025
Answered

How to batch aligning text and scaling the shape in photoshop

Hello everyone! I'm having trouble with many text layers and shape layers that need to be aligned and scaled. Doing this manually would take a lot of time, so I want to write a script to help automate the process. I previously tried using AI for this, but it didn't work well.

All text and shape layers are grouped together, and sometimes their order isn't aligned properly. Usually, I select the text layer and the shape below it manually, center them, and then scale the shape. I do this by selecting the text, expanding it by a certain number (e.g., 100px, 45px), and scaling the shape to cover it while maintaining its proportions.

Not all text layers have a shape below them, so I will skip those. I've attached a PSD sample for reference.

Does anyone have any suggestions? If you can help, I would greatly appreciate it. Thank you for your interest in this topic!

 

 

 

 

c.pfaffenbichler
c.pfaffenbichlerCorrect answer
Community Expert
March 19, 2025

// scale shape layers beneath type layers to their width plus two times theAdd;
// 2025, use it at your own risk;
if (app.documents.length > 0) {
var myDocument = app.activeDocument;
var originalRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
// horizontal offset;
var theAdd = 100;
var theVertAdd = 171;
// collect type layers;
var theLayers = collectTypeLayers ();
var theCounter = 0;
// check for shape layers;
for (var m = 0; m < theLayers.length; m++) {
    var thisOne = theLayers[m];
    var ref = new ActionReference();
    ref.putIndex( charIDToTypeID("Lyr "), theLayers[m][1]-1/*+theCounter*/ ); 
    var layerDesc = executeActionGet(ref);
    var layerKind = layerDesc.getInteger(stringIDToTypeID("layerKind"));
    if (layerKind == 4) {
        var thisID = layerDesc.getInteger(stringIDToTypeID("layerID"));
        var theBoundsNoFx = layerDesc.getObjectValue(stringIDToTypeID("boundsNoEffects"));
        var X1 = theBoundsNoFx.getUnitDoubleValue(stringIDToTypeID("left"));
        var X2 = theBoundsNoFx.getUnitDoubleValue(stringIDToTypeID("right"));
        var Y1 = theBoundsNoFx.getUnitDoubleValue(stringIDToTypeID("top"));
        var Y2 = theBoundsNoFx.getUnitDoubleValue(stringIDToTypeID("bottom"));
        var theW = theBoundsNoFx.getUnitDoubleValue(stringIDToTypeID("width"));
        var theH = theBoundsNoFx.getUnitDoubleValue(stringIDToTypeID("height"));
        var xOffset = (thisOne[3][0]+thisOne[4]/2) - (X1+theW/2);
        var yOffset = (thisOne[3][1]+thisOne[5]/2) - (Y1+theH/2);
//        var theScale = ((thisOne[4]+2*theAdd)/theW*100);// Math((thisOne[5]+2*theAdd)/theH*100)
        var theScale = Math.max(((thisOne[4]+2*theAdd)/theW*100), ((thisOne[5]+2*theVertAdd)/theH*100));
        layerDuplicateScaleRotate (thisID, false, xOffset, yOffset, theScale, theScale, 0);
        theCounter++
    }
};
// reset;
app.preferences.rulerUnits = originalRulerUnits;
};
////////////////////////////////////
function collectTypeLayers () {
// 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 ) {

if (layerDesc.hasKey(stringIDToTypeID('smartObject')) == true || layerDesc.hasKey(stringIDToTypeID("textKey")) == true) {

var theName = layerDesc.getString(stringIDToTypeID('name'));
var theID = layerDesc.getInteger(stringIDToTypeID('layerID'));
var theIndex = layerDesc.getInteger(stringIDToTypeID('itemIndex'));

var theBounds = layerDesc.getObjectValue(stringIDToTypeID("bounds"));
var theBoundsNoFx = layerDesc.getObjectValue(stringIDToTypeID("boundsNoEffects"));
var X1 = theBoundsNoFx.getUnitDoubleValue(stringIDToTypeID("left"));
var X2 = theBoundsNoFx.getUnitDoubleValue(stringIDToTypeID("right"));
var Y1 = theBoundsNoFx.getUnitDoubleValue(stringIDToTypeID("top"));
var Y2 = theBoundsNoFx.getUnitDoubleValue(stringIDToTypeID("bottom"));
var theW = theBoundsNoFx.getUnitDoubleValue(stringIDToTypeID("width"));
var theH = theBoundsNoFx.getUnitDoubleValue(stringIDToTypeID("height"));

theLayers.push([theName, theIndex, theID, [X1, Y1, X2, Y2], theW, theH])
}
};
}
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); 
}
};
////// duplicate layer (id, xOffset, yOffset, theXScale, theYScale, theAngle) //////
function layerDuplicateScaleRotate (theID, theDuplicate, xOffset, yOffset, theXScale, theYScale, theAngle) {
selectLayerByID(theID, false);
// =======================================================
var desc23 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref2 = new ActionReference();
ref2.putIdentifier ( charIDToTypeID( "Lyr " ), theID );
//        ref2.putEnumerated( charIDToTypeID( "Lyr " ), charIDToTypeID( "Ordn" ), charIDToTypeID( "Trgt" ) );
desc23.putReference( idnull, ref2 );
desc23.putEnumerated( charIDToTypeID( "FTcs" ), charIDToTypeID( "QCSt" ), charIDToTypeID( "Qcsa" ) );
var idOfst = charIDToTypeID( "Ofst" );
var desc24 = new ActionDescriptor();
var idPxl = charIDToTypeID( "#Pxl" );
desc24.putUnitDouble( charIDToTypeID( "Hrzn" ), idPxl, xOffset );
desc24.putUnitDouble( charIDToTypeID( "Vrtc" ), idPxl, yOffset );
var idOfst = charIDToTypeID( "Ofst" );
desc23.putObject( idOfst, idOfst, desc24 );
var idPrc = charIDToTypeID( "#Prc" );
desc23.putUnitDouble( charIDToTypeID( "Wdth" ), idPrc, theXScale );
desc23.putUnitDouble( charIDToTypeID( "Hght" ), idPrc, theYScale );
desc23.putUnitDouble( charIDToTypeID( "Angl" ), charIDToTypeID( "#Ang" ), theAngle );
desc23.putEnumerated( charIDToTypeID( "Intr" ), charIDToTypeID( "Intp" ), stringIDToTypeID( "bicubicAutomatic" ) );
desc23.putBoolean( charIDToTypeID( "Cpy " ), theDuplicate );
executeAction( charIDToTypeID( "Trnf" ), desc23, DialogModes.NO );
};

edited 2025-03-20

11 replies

c.pfaffenbichler
Community Expert
c.pfaffenbichlerCommunity ExpertCorrect answer
Community Expert
March 19, 2025

// scale shape layers beneath type layers to their width plus two times theAdd;
// 2025, use it at your own risk;
if (app.documents.length > 0) {
var myDocument = app.activeDocument;
var originalRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
// horizontal offset;
var theAdd = 100;
var theVertAdd = 171;
// collect type layers;
var theLayers = collectTypeLayers ();
var theCounter = 0;
// check for shape layers;
for (var m = 0; m < theLayers.length; m++) {
    var thisOne = theLayers[m];
    var ref = new ActionReference();
    ref.putIndex( charIDToTypeID("Lyr "), theLayers[m][1]-1/*+theCounter*/ ); 
    var layerDesc = executeActionGet(ref);
    var layerKind = layerDesc.getInteger(stringIDToTypeID("layerKind"));
    if (layerKind == 4) {
        var thisID = layerDesc.getInteger(stringIDToTypeID("layerID"));
        var theBoundsNoFx = layerDesc.getObjectValue(stringIDToTypeID("boundsNoEffects"));
        var X1 = theBoundsNoFx.getUnitDoubleValue(stringIDToTypeID("left"));
        var X2 = theBoundsNoFx.getUnitDoubleValue(stringIDToTypeID("right"));
        var Y1 = theBoundsNoFx.getUnitDoubleValue(stringIDToTypeID("top"));
        var Y2 = theBoundsNoFx.getUnitDoubleValue(stringIDToTypeID("bottom"));
        var theW = theBoundsNoFx.getUnitDoubleValue(stringIDToTypeID("width"));
        var theH = theBoundsNoFx.getUnitDoubleValue(stringIDToTypeID("height"));
        var xOffset = (thisOne[3][0]+thisOne[4]/2) - (X1+theW/2);
        var yOffset = (thisOne[3][1]+thisOne[5]/2) - (Y1+theH/2);
//        var theScale = ((thisOne[4]+2*theAdd)/theW*100);// Math((thisOne[5]+2*theAdd)/theH*100)
        var theScale = Math.max(((thisOne[4]+2*theAdd)/theW*100), ((thisOne[5]+2*theVertAdd)/theH*100));
        layerDuplicateScaleRotate (thisID, false, xOffset, yOffset, theScale, theScale, 0);
        theCounter++
    }
};
// reset;
app.preferences.rulerUnits = originalRulerUnits;
};
////////////////////////////////////
function collectTypeLayers () {
// 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 ) {

if (layerDesc.hasKey(stringIDToTypeID('smartObject')) == true || layerDesc.hasKey(stringIDToTypeID("textKey")) == true) {

var theName = layerDesc.getString(stringIDToTypeID('name'));
var theID = layerDesc.getInteger(stringIDToTypeID('layerID'));
var theIndex = layerDesc.getInteger(stringIDToTypeID('itemIndex'));

var theBounds = layerDesc.getObjectValue(stringIDToTypeID("bounds"));
var theBoundsNoFx = layerDesc.getObjectValue(stringIDToTypeID("boundsNoEffects"));
var X1 = theBoundsNoFx.getUnitDoubleValue(stringIDToTypeID("left"));
var X2 = theBoundsNoFx.getUnitDoubleValue(stringIDToTypeID("right"));
var Y1 = theBoundsNoFx.getUnitDoubleValue(stringIDToTypeID("top"));
var Y2 = theBoundsNoFx.getUnitDoubleValue(stringIDToTypeID("bottom"));
var theW = theBoundsNoFx.getUnitDoubleValue(stringIDToTypeID("width"));
var theH = theBoundsNoFx.getUnitDoubleValue(stringIDToTypeID("height"));

theLayers.push([theName, theIndex, theID, [X1, Y1, X2, Y2], theW, theH])
}
};
}
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); 
}
};
////// duplicate layer (id, xOffset, yOffset, theXScale, theYScale, theAngle) //////
function layerDuplicateScaleRotate (theID, theDuplicate, xOffset, yOffset, theXScale, theYScale, theAngle) {
selectLayerByID(theID, false);
// =======================================================
var desc23 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref2 = new ActionReference();
ref2.putIdentifier ( charIDToTypeID( "Lyr " ), theID );
//        ref2.putEnumerated( charIDToTypeID( "Lyr " ), charIDToTypeID( "Ordn" ), charIDToTypeID( "Trgt" ) );
desc23.putReference( idnull, ref2 );
desc23.putEnumerated( charIDToTypeID( "FTcs" ), charIDToTypeID( "QCSt" ), charIDToTypeID( "Qcsa" ) );
var idOfst = charIDToTypeID( "Ofst" );
var desc24 = new ActionDescriptor();
var idPxl = charIDToTypeID( "#Pxl" );
desc24.putUnitDouble( charIDToTypeID( "Hrzn" ), idPxl, xOffset );
desc24.putUnitDouble( charIDToTypeID( "Vrtc" ), idPxl, yOffset );
var idOfst = charIDToTypeID( "Ofst" );
desc23.putObject( idOfst, idOfst, desc24 );
var idPrc = charIDToTypeID( "#Prc" );
desc23.putUnitDouble( charIDToTypeID( "Wdth" ), idPrc, theXScale );
desc23.putUnitDouble( charIDToTypeID( "Hght" ), idPrc, theYScale );
desc23.putUnitDouble( charIDToTypeID( "Angl" ), charIDToTypeID( "#Ang" ), theAngle );
desc23.putEnumerated( charIDToTypeID( "Intr" ), charIDToTypeID( "Intp" ), stringIDToTypeID( "bicubicAutomatic" ) );
desc23.putBoolean( charIDToTypeID( "Cpy " ), theDuplicate );
executeAction( charIDToTypeID( "Trnf" ), desc23, DialogModes.NO );
};

edited 2025-03-20

Translate
Alice392Author
Participating Frequently
March 19, 2025

I truly appreciate all your support—thank you! Your help means a lot to me!  😄 😄 😄

Translate
c.pfaffenbichler
Community Expert
Community Expert
March 20, 2025

I am afraid I overlooked a point – I only conditioned the scaling on the width of the type layer. 

You actually mentioned a vertial distance, too, but I suppose in the sample file all the type layers were fairly wide anyway so it didn’t make a difference. 

I hope I’ll get around to amending the code in the coming days. 

Translate
Alice392Author
Participating Frequently
March 18, 2025

Thank you so much for your reply. I have seen the post and tried to apply it to my workflow, but it only works with rasterized layers. I need to preserve the shape path and cannot use a smart object, as my client specifically requested to keep the shape intact.
I attempted to use AI for customization, but it didn’t turn out well. Additionally, the code you provided is meant for text layers that have a stroke applied. However, I have many layer styles that I need to use, so applying a stroke is not an option for me. Instead, I am using selection tools to deselect layers more quickly. Could you please help me with this? I would greatly appreciate your assistance!

Translate
Alice392Author
Participating Frequently
March 17, 2025

here is an example

Translate