Copy link to clipboard
Copied
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!
// 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++) {
...
Copy link to clipboard
Copied
here is an example
Copy link to clipboard
Copied
Copy link to clipboard
Copied
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!
Copy link to clipboard
Copied
// 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
Copy link to clipboard
Copied
I truly appreciate all your support—thank you! Your help means a lot to me! 😄 😄 😄
Copy link to clipboard
Copied
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.
Copy link to clipboard
Copied
Absolutely! I would be so grateful if you could lend a hand with fixing the code sometime in the coming days. Thank you so much for your concern.
Copy link to clipboard
Copied
I updated the code, I hope this meets the original intention (see screenshots).
Copy link to clipboard
Copied
Thank you for your help, it works wonderfully. If possible, could you assist me in adding a condition so that if the horizontal and vertical additions are equal, the shape will scale disproportionately? It looks like this picture, it will help me to work more flexiable. I really appreciate it!
Copy link to clipboard
Copied
// 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 = 101;
var theVertAdd = 101;
// 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);
if (theAdd != theVertAdd) {
var theXScale = Math.max(((thisOne[4]+2*theAdd)/theW*100), ((thisOne[5]+2*theVertAdd)/theH*100));
var theYScale = theXScale
}
else {
var theXScale = ((thisOne[4]+2*theAdd)/theW*100)
var theYScale = ((thisOne[5]+2*theVertAdd)/theH*100);
};
layerDuplicateScaleRotate (thisID, false, xOffset, yOffset, theXScale, theYScale, 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 );
};
Copy link to clipboard
Copied
I sincerely appreciate your help! These tools fit so well into my workflow and have made a huge difference in my productivity. Thank you once again for your support!