Skip to main content
Inspiring
April 11, 2022
Answered

A way to scale multiple smart objects to fit set boundary with their first non-empty pixels?

  • April 11, 2022
  • 2 replies
  • 2018 views

I have a lot of work to do where i have many different smart objects, that dont always fill their entire document sizes (have a lot of empty space inside them). My workflow requires putting them all inside the document on different layers, and then moving and scaling them to fit the set boundaries. What I am missing is a way to automatically scale them all up, to individually optimal sizes, so their content (not entire smart object) scales to fit set boundary area inside PSD or at least the document they are placed in, so i can then scale them all together in standard way (right now placed PSDs as smart objects automatically try to fit the document dimensions they are placed in, but take into calculation also the unwanted empty space inside them).

In short, something similar to centering multiple smart objects vertically and horizontally (which takes into account only non-empty pixels to determine the object's real content), but something that would also scale to fit into area i set for it or at least scale it up to fit document they were placed into, disregarding the empty space inside them.

Does anyone know any script that would do that?

 

edit: Functionality like Photoshop's "resize image during place" in general settings is what would help, if only there was a way to make it ignore empty pixels of placed objects.

 

edit2: Current workaround for me is to first run through the documents that i am goign to place into my main PSD, and automatically export them with "trim" enabled. Then after placing them, they are all placed with the default "fit" to document size. Would prefer a way at least without that step, as it seems pretty easy to script, considering the photoshop's centering vertically/horizontally already ignores empty pixels in smart objects.

 

Thanks a lot in advance!

This topic has been closed for replies.
Correct answer c.pfaffenbichler

You can give this a try. 

// place smart objects and scale them to selection or document bounds;
// 2022, use it at your own risk;
if (app.documents.length > 0) {
var theFiles = selectFile(true);
// set to pixels;
var originalRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
////////////////////////////////////
if (hasSelection() == true) {
var selBounds = activeDocument.selection.bounds;
var docWidth = selBounds[2] - selBounds[0];
var docHeight = selBounds[3] - selBounds[1];
var theX = selBounds[0] + docWidth/2;
var theY = selBounds[1] + docHeight/2;
}
else {
var docWidth = activeDocument.width;
var docHeight = activeDocument.height;
var theX = docWidth/2;
var theY = docHeight/2;
};
////////////////////////////////////
for (var x = 0; x < theFiles.length; x++) {
placeScaleRotateFile (theFiles[x], 0, 0, 100, 100, 0, false);
////////////////////////////////////
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
var layerDesc = executeActionGet(ref);
var isSmartObject = layerDesc.hasKey(stringIDToTypeID("smartObject"));
if (isSmartObject == true) {
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 theW = theBounds.getUnitDoubleValue(stringIDToTypeID("right")) - theBounds.getUnitDoubleValue(stringIDToTypeID("left"));
var theH = theBounds.getUnitDoubleValue(stringIDToTypeID("bottom")) - theBounds.getUnitDoubleValue(stringIDToTypeID("top"));
var horCenter = theseBounds[0] + theW / 2;
var verCenter = theseBounds[1] + theH / 2;
layerBounds = [theseBounds, theW, theH, horCenter, verCenter];
////////////////////////////////////
if (docWidth / docHeight < layerBounds[1] / layerBounds[2]) {var theScale = docWidth / layerBounds[1] * 100}
else {var theScale = docHeight / layerBounds[2] * 100};
var xOffset = theX - layerBounds[3];
var yOffset = theY - layerBounds[4];
////////////////////////////////////
var idtransform = stringIDToTypeID( "transform" );
var idpixelsUnit = stringIDToTypeID( "pixelsUnit" );
var idpercentUnit = stringIDToTypeID( "percentUnit" );
var idoffset = stringIDToTypeID( "offset" );
var desc5 = new ActionDescriptor();
    desc5.putEnumerated( stringIDToTypeID( "freeTransformCenterState" ), stringIDToTypeID( "quadCenterState" ), stringIDToTypeID( "QCSAverage" ) );
        var desc6 = new ActionDescriptor();
        desc6.putUnitDouble( stringIDToTypeID( "horizontal" ), idpixelsUnit, xOffset );
        desc6.putUnitDouble( stringIDToTypeID( "vertical" ), idpixelsUnit, yOffset );
    desc5.putObject( idoffset, idoffset, desc6 );
    desc5.putUnitDouble( stringIDToTypeID( "width" ), idpercentUnit, theScale );
    desc5.putUnitDouble( stringIDToTypeID( "height" ), idpercentUnit, theScale );
executeAction( idtransform, desc5, DialogModes.NO );
};
};
// reset;
app.preferences.rulerUnits = originalRulerUnits;
};
////////////////////////////////////
////// bounds of active layer //////
function getBounds () {
    var ref = new ActionReference();
    ref.putProperty (stringIDToTypeID ("property"), stringIDToTypeID ("bounds"));
	ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
    var layerDesc = executeActionGet(ref);
    var theseBounds = [theBounds.getUnitDoubleValue(stringIDToTypeID("left")), theBounds.getUnitDoubleValue(stringIDToTypeID("top")), theBounds.getUnitDoubleValue(stringIDToTypeID("right")), theBounds.getUnitDoubleValue(stringIDToTypeID("bottom"))];
    var theW = theBounds.getUnitDoubleValue(stringIDToTypeID("right")) - theBounds.getUnitDoubleValue(stringIDToTypeID("left"));
    var theH = theBounds.getUnitDoubleValue(stringIDToTypeID("bottom")) - theBounds.getUnitDoubleValue(stringIDToTypeID("top"));
    var horCenter = theseBounds[0] + theW / 2;
    var verCenter = theseBounds[1] + theH / 2;
    return ([theseBounds, theW, theH, horCenter, verCenter])
};
////// 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 layerID;
    };
////// 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
};
////// check for selection //////
function hasSelection(){
    var ref10 = new ActionReference();
    ref10.putProperty(stringIDToTypeID("property"), stringIDToTypeID("selection"));
    ref10.putEnumerated( charIDToTypeID( "Dcmn" ), charIDToTypeID( "Ordn" ), charIDToTypeID( "Trgt" ) );
    var docDesc = executeActionGet(ref10);
    return docDesc.hasKey(stringIDToTypeID("selection"));
    };

2 replies

c.pfaffenbichler
Community Expert
Community Expert
April 12, 2022

edited

 

 

FalrinthAuthor
Inspiring
April 12, 2022

Thank you. I will try to play with it in next free moment (cant test right now).

I am not very familiar with Photoshop DOM or the scripting. I mean i somewhat understund the script when i read it, and i am already using some PS scripts in my work, but i am no programmer.

 

To answer your question about how would i like the automated workflow to work at this step (i will write the close-to-perfect solution here):

- I would drag multiple images with varied sizes and empty space inside them into photoshop document

- I would select all these new layers with placed images

- I would select box on the document

- I would like to use script to make these images fit into the selected box (or a rectangular shape on different layer if thats easier to script), ignoring empty space of these smart objects. So basically center non-empty pixels content for each selected layer's inside the box (PS can do that already) and scale them individually inside that box to touch closest border, be it vertical or horizontal (to fit with maximum possible size of each smartobject, but dont go beyond selected box or rectangular boundary with objects' non-empty pixels)

 

c.pfaffenbichler
Community Expert
c.pfaffenbichlerCommunity ExpertCorrect answer
Community Expert
April 12, 2022

You can give this a try. 

// place smart objects and scale them to selection or document bounds;
// 2022, use it at your own risk;
if (app.documents.length > 0) {
var theFiles = selectFile(true);
// set to pixels;
var originalRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
////////////////////////////////////
if (hasSelection() == true) {
var selBounds = activeDocument.selection.bounds;
var docWidth = selBounds[2] - selBounds[0];
var docHeight = selBounds[3] - selBounds[1];
var theX = selBounds[0] + docWidth/2;
var theY = selBounds[1] + docHeight/2;
}
else {
var docWidth = activeDocument.width;
var docHeight = activeDocument.height;
var theX = docWidth/2;
var theY = docHeight/2;
};
////////////////////////////////////
for (var x = 0; x < theFiles.length; x++) {
placeScaleRotateFile (theFiles[x], 0, 0, 100, 100, 0, false);
////////////////////////////////////
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
var layerDesc = executeActionGet(ref);
var isSmartObject = layerDesc.hasKey(stringIDToTypeID("smartObject"));
if (isSmartObject == true) {
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 theW = theBounds.getUnitDoubleValue(stringIDToTypeID("right")) - theBounds.getUnitDoubleValue(stringIDToTypeID("left"));
var theH = theBounds.getUnitDoubleValue(stringIDToTypeID("bottom")) - theBounds.getUnitDoubleValue(stringIDToTypeID("top"));
var horCenter = theseBounds[0] + theW / 2;
var verCenter = theseBounds[1] + theH / 2;
layerBounds = [theseBounds, theW, theH, horCenter, verCenter];
////////////////////////////////////
if (docWidth / docHeight < layerBounds[1] / layerBounds[2]) {var theScale = docWidth / layerBounds[1] * 100}
else {var theScale = docHeight / layerBounds[2] * 100};
var xOffset = theX - layerBounds[3];
var yOffset = theY - layerBounds[4];
////////////////////////////////////
var idtransform = stringIDToTypeID( "transform" );
var idpixelsUnit = stringIDToTypeID( "pixelsUnit" );
var idpercentUnit = stringIDToTypeID( "percentUnit" );
var idoffset = stringIDToTypeID( "offset" );
var desc5 = new ActionDescriptor();
    desc5.putEnumerated( stringIDToTypeID( "freeTransformCenterState" ), stringIDToTypeID( "quadCenterState" ), stringIDToTypeID( "QCSAverage" ) );
        var desc6 = new ActionDescriptor();
        desc6.putUnitDouble( stringIDToTypeID( "horizontal" ), idpixelsUnit, xOffset );
        desc6.putUnitDouble( stringIDToTypeID( "vertical" ), idpixelsUnit, yOffset );
    desc5.putObject( idoffset, idoffset, desc6 );
    desc5.putUnitDouble( stringIDToTypeID( "width" ), idpercentUnit, theScale );
    desc5.putUnitDouble( stringIDToTypeID( "height" ), idpercentUnit, theScale );
executeAction( idtransform, desc5, DialogModes.NO );
};
};
// reset;
app.preferences.rulerUnits = originalRulerUnits;
};
////////////////////////////////////
////// bounds of active layer //////
function getBounds () {
    var ref = new ActionReference();
    ref.putProperty (stringIDToTypeID ("property"), stringIDToTypeID ("bounds"));
	ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
    var layerDesc = executeActionGet(ref);
    var theseBounds = [theBounds.getUnitDoubleValue(stringIDToTypeID("left")), theBounds.getUnitDoubleValue(stringIDToTypeID("top")), theBounds.getUnitDoubleValue(stringIDToTypeID("right")), theBounds.getUnitDoubleValue(stringIDToTypeID("bottom"))];
    var theW = theBounds.getUnitDoubleValue(stringIDToTypeID("right")) - theBounds.getUnitDoubleValue(stringIDToTypeID("left"));
    var theH = theBounds.getUnitDoubleValue(stringIDToTypeID("bottom")) - theBounds.getUnitDoubleValue(stringIDToTypeID("top"));
    var horCenter = theseBounds[0] + theW / 2;
    var verCenter = theseBounds[1] + theH / 2;
    return ([theseBounds, theW, theH, horCenter, verCenter])
};
////// 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 layerID;
    };
////// 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
};
////// check for selection //////
function hasSelection(){
    var ref10 = new ActionReference();
    ref10.putProperty(stringIDToTypeID("property"), stringIDToTypeID("selection"));
    ref10.putEnumerated( charIDToTypeID( "Dcmn" ), charIDToTypeID( "Ordn" ), charIDToTypeID( "Trgt" ) );
    var docDesc = executeActionGet(ref10);
    return docDesc.hasKey(stringIDToTypeID("selection"));
    };
c.pfaffenbichler
Community Expert
Community Expert
April 12, 2022

By grouping a Smart Object and transforming the Group one can get at the Bounding Box of the SO’s pixel content. 

 

How frequent is the issue for you? 

How familiar are you with JavaScript and Photoshop’s DOM? 

 

Edit: How exactly doe you want to automate the task in your workflow?