Skip to main content
X-Raym
Participating Frequently
February 21, 2018
Answered

Get Layer Transform X Y Top Left Coordinate Position

  • February 21, 2018
  • 3 replies
  • 15080 views

Hi !

I try to look for this but in fact it seems more complicated than expected, and I didn't find a working up to date solution.

So we have layer bound property, which can return top left value of a layer.

But if the layer is smart layer (or a text) this may not work as I would have expect: indeed, it will return the value of the position of the first pixel, and not the actual smart layer position (determined by it's original boundaries).

Here is a screen capture to show the difference between the layer top left coordinate and the transform X Y position (which match the original file placement):

How do you get these X Y positions from a Photoshop script ?

Thanks !

This topic has been closed for replies.
Correct answer r-bin

Try this function

function get_layer_bounds(layer)

    {

    try

        {

        if (layer.kind == LayerKind.SMARTOBJECT)

            {

            var layer0 = activeDocument.activeLayer;

            activeDocument.activeLayer = layer;

            var r = new ActionReference();

            r.putEnumerated( charIDToTypeID( "Lyr " ), charIDToTypeID( "Ordn" ), charIDToTypeID( "Trgt" ) );

            var d = executeActionGet(r).getObjectValue(stringIDToTypeID("smartObjectMore")).getList(stringIDToTypeID("transform"));

            var x = [d.getDouble(0), d.getDouble(2), d.getDouble(4), d.getDouble(6)];

            var y = [d.getDouble(1), d.getDouble(3), d.getDouble(5), d.getDouble(7)];

          

            var l = [Math.min(x[0], Math.min(x[1], Math.min(x[2], x[3]))) ];

            var r = [Math.max(x[0], Math.max(x[1], Math.max(x[2], x[3]))) ];

            var t = [Math.min(y[0], Math.min(y[1], Math.min(y[2], y[3]))) ];

            var b = [Math.max(y[0], Math.max(y[1], Math.max(y[2], y[3]))) ];

            activeDocument.activeLayer = layer0;

            return [ UnitValue(l,"px"), UnitValue(t,"px"), UnitValue(r,"px"), UnitValue(b,"px") ];

            }

        else

            return layer.boundsNoEffects;

        }

    catch (e) { alert(e); }

    }

3 replies

X-Raym
X-RaymAuthor
Participating Frequently
March 17, 2018

r-bin​, the question is for anyone who want to answer 🙂
Thank you again for your time and expertise !!

 

I'll see how this function can be integrated in my current script.

 

Cheers !

X-Raym
X-RaymAuthor
Participating Frequently
March 15, 2018

I just got a very particular cases,

How would you get X and Y position relative to the Artboard position ? Seems to be a quite complex API case.

I had a layer on a artboard which was offset by 961px, and all Y were offset by 961px. Idealy, I would prefer to have it not offset.

Thanks for your help

Legend
March 16, 2018

If the question is to me, then I have never worked with ArtBoards (in CS6 there are none).

But if I understand correctly what you need, then here is a new function which, depending on the parameter "in_artboard" returns the absolute or relative coordinates of the layer (or smart object).

 

To work, you also need the function get_prop_value().

Its code can be found in this thread: Modifying Levels Adjustment Layer?

 

alert(get_layer_bounds(activeDocument.activeLayer, false))

alert(get_layer_bounds(activeDocument.activeLayer, true))

 

function get_layer_bounds(layer, in_artboard) 

    { 

    try 

        { 

        var ret;

 

        var layer0 = activeDocument.activeLayer; 

        activeDocument.activeLayer = layer; 

 

        var id = get_prop_value("layer", null, "layerID");

 

        if (layer.kind == LayerKind.SMARTOBJECT) 

            { 

            var d = get_prop_value("layer", null, "smartObjectMore", "transform");

 

            var x = [d.getDouble(0), d.getDouble(2), d.getDouble(4), d.getDouble(6)]; 

            var y = [d.getDouble(1), d.getDouble(3), d.getDouble(5), d.getDouble(7)]; 

            

            var l = [Math.min(x[0], Math.min(x[1], Math.min(x[2], x[3]))) ]; 

            var r = [Math.max(x[0], Math.max(x[1], Math.max(x[2], x[3]))) ]; 

 

            var t = [Math.min(y[0], Math.min(y[1], Math.min(y[2], y[3]))) ]; 

            var b = [Math.max(y[0], Math.max(y[1], Math.max(y[2], y[3]))) ]; 

 

            ret = [ UnitValue(l,"px"), UnitValue(t,"px"), UnitValue(r,"px"), UnitValue(b,"px") ]; 

            } 

        else 

            {

            var old_units = app.preferences.rulerUnits;

            app.preferences.rulerUnits = Units.PIXELS;

 

            ret = layer.boundsNoEffects; 

            app.preferences.rulerUnits = old_units;

            }

 

        if (in_artboard)

            {

            var art_rect = get_layer_atrboard_rect(id)

   

            ret[0] -= art_rect.left;

            ret[2] -= art_rect.left;

 

            ret[1] -= art_rect.top;

            ret[3] -= art_rect.top;

            }

 

        activeDocument.activeLayer = layer0; 

 

        return ret;

        } 

    catch (e) { alert(e); } 

    }  

 

function get_layer_atrboard_rect(id)

    {

    try {

        var not_artboard_rect = {top:0, left:0, bottom:0, right:0};

 

        eval("var json = " + get_prop_value("layer", null, "json"));

 

        if (json == undefined) return not_artboard_rect;

 

        function test_id(layer, id, n)

            {

            if (layer.id == id) return n;

 

            if (layer.layers)

                {

                for (var i = 0; i < layer.layers.length; i++)

                    {

                    var ret = test_id(layer.layers, id, n+1);

                    if (ret != null) return ret;

                    }               

                return null;

                }

            else

                {

                return null;

                }           

            }

 

        for (var i = 0; i < json.layers.length; i++)

            {

            var ret = test_id(json.layers, id, 0);

 

            if (ret)

                {

                if (json.layers.artboard) return json.layers.artboard.artboardRect;

                else return not_artboard_rect;

                break;       

                }

            }

       

        return not_artboard_rect;       

        }

    catch (e) { alert(e); }

    }

Legend
February 21, 2018
X-Raym
X-RaymAuthor
Participating Frequently
February 21, 2018

r-bin

Hi, thanks for your very quick assistance (and your initial code snippet) !

This get_smart_object_corners() function doesn't take any parameter, how come ?

How to pass a Layer as parameter to it ?

My ultimate goal is to merge this code snippet to

Adobe-Export-Scripts/Export Layer Coordinates Photoshop.jsx at master · bronzehedwick/Adobe-Export-Scripts · GitHub

As you can see L44, the function need to take a Layer as parameter.

Many thanks again!

(let me know if you prefer me to answer in the original thread).

Legend
February 22, 2018

r-bin  wrote

And also explore the "size" and "warp" objects in the smartObjectMore object.

I do not have time for this now )

I you get some time could you code  functions that one could use to explore a smart objects size and warp.  Like you did for  smart object layers object corners.  As I wrote I can hack some but  I lack the knowledge to use  action manager code to retrieve Photoshop layer related data.  I needed you to tell me to change to "nonAffineTransform" to get the corners I wanted.  You knowledge of what Photoshop has and how to get at it is knowledge I do not expect to acquire in my lifetime I'm 77 and slowing down.  Your four corners function help me do some smart object layer exploring. Thank you

// Explore Smart Object Layer's 4 corner-points (x,y)

/////////////////////////////////////////////////////

if (app.activeDocument.activeLayer.kind!=LayerKind.SMARTOBJECT) alert("ActiveLayer not Smart Object Layer");

else {

var orig_ruler_units = app.preferences.rulerUnits;

app.preferences.rulerUnits = Units.PIXELS; // Set the ruler units to PIXELS

clearGuides();                              // Clear Canvas Guides

var points = get_smart_object_corners();    // Get Smart Object layers corners

app.activeDocument.selection.select(points);// Set new selection  using layers corners

    // Mark their (x,y)

MarkX( points[0][0]);

MarkY( points[0][1]);

MarkX( points[1][0]);

MarkY( points[1][1]);

MarkX( points[2][0]);

MarkY( points[2][1]);

MarkX( points[3][0]);

MarkY( points[3][1]);

// get the slope and length of the layers sides 0 is horizontal 90 is vertical

var w = points[0][0] - points[1][0];

var h = points[0][1] - points[1][1];

var l0 = Math.sqrt(Math.pow(w,2)+Math.pow(h,2));

var angle0 = Math.atan(h/w) * 180.0 / Math.PI;

var w = points[1][0] - points[2][0] ;

var h = points[1][1] - points[2][1] ;

var l1 = Math.sqrt(Math.pow(w,2)+Math.pow(h,2));

var angle1 = Math.atan(h/w) * 180.0 / Math.PI;

var w = points[2][0] - points[3][0] ;

var h = points[2][1] - points[3][1] ;

var l2 = Math.sqrt(Math.pow(w,2)+Math.pow(h,2));

var angle2 = Math.atan(h/w) * 180.0 / Math.PI;

var w = points[3][0] - points[0][0] ;

var h = points[3][1] - points[0][1] ;

var l3 = Math.sqrt(Math.pow(w,2)+Math.pow(h,2));

var angle3 = Math.atan(h/w) * 180.0 / Math.PI;

//prerimmeter length

var l4 = l0+l1+l2+l3;

    //show the user this information

alert( Math.round(points[0][0]) + ", " +  Math.round(points[0][1]) + " "

+ angle0.](0) + "° " + l0.toFixed(2) +"\n\n"

+ Math.round(points[1][0]) + ", " +  Math.round(points[1][1]) + " "

  + angle1.toFixed(1) + "° " + l1.toFixed(2) +"\n\n"

+ Math.round(points[2][0]) + ", " +  Math.round(points[2][1]) + " "

+ angle2.toFixed(1) + "° " + l2.toFixed(2) +"\n\n"

+ Math.round(points[3][0]) + ", " +  Math.round(points[3][1]) + " "

+ angle3.toFixed(1) + "° " + l3.toFixed(2) +"\n\n"

+ l4.toFixed(2));

app.preferences.rulerUnits = orig_ruler_units; // Reset units to original settings

}

////////////////////////////////////////////////////////////////////////////////////////////////////////

// function get_smart_object_corners() returns array from 4 corner-points (x,y)

// of smart object in pixels (value with floating point) https://forums.adobe.com/people/r-bin

////////////////////////////////////////////////////////////////////////////////////////////////////////

function get_smart_object_corners() {

    try

        {

        var r = new ActionReference();

        r.putEnumerated( charIDToTypeID( "Lyr " ), charIDToTypeID( "Ordn" ), charIDToTypeID( "Trgt" ) );

        var d;

        try { d = executeActionGet(r); } catch (e) { alert(e); return; }

        try { d = d.getObjectValue(stringIDToTypeID("smartObjectMore"));    } catch (e) { alert(e); return; }

        try { d = d.getList(stringIDToTypeID("nonAffineTransform"));                 } catch (e) { alert(e); return; }

        var ret = [[d.getDouble(0),d.getDouble(1)],

                   [d.getDouble(2),d.getDouble(3)],

                   [d.getDouble(4),d.getDouble(5)],

                   [d.getDouble(6),d.getDouble(7)]];

        return ret;

        }

    catch (e) { alert(e); }

}

function clearGuides() {

   var id556 = charIDToTypeID( "Dlt " );

       var desc102 = new ActionDescriptor();

       var id557 = charIDToTypeID( "null" );

           var ref70 = new ActionReference();

           var id558 = charIDToTypeID( "Gd  " );

           var id559 = charIDToTypeID( "Ordn" );

           var id560 = charIDToTypeID( "Al  " );

           ref70.putEnumerated( id558, id559, id560 );

       desc102.putReference( id557, ref70 );

   executeAction( id556, desc102, DialogModes.NO );

};

function MarkX(x) {

// =======================================================

var idMk = charIDToTypeID( "Mk  " );

    var desc61 = new ActionDescriptor();

    var idNw = charIDToTypeID( "Nw  " );

        var desc62 = new ActionDescriptor();

        var idPstn = charIDToTypeID( "Pstn" );

        var idPxl = charIDToTypeID( "#Pxl" );

        desc62.putUnitDouble( idPstn, idPxl, x);

        var idOrnt = charIDToTypeID( "Ornt" );

        var idOrnt = charIDToTypeID( "Ornt" );

        var idVrtc = charIDToTypeID( "Vrtc" );

        desc62.putEnumerated( idOrnt, idOrnt, idVrtc );

    var idGd = charIDToTypeID( "Gd  " );

    desc61.putObject( idNw, idGd, desc62 );

executeAction( idMk, desc61, DialogModes.NO );

}

function MarkY(y) {

// =======================================================

var idMk = charIDToTypeID( "Mk  " );

    var desc63 = new ActionDescriptor();

    var idNw = charIDToTypeID( "Nw  " );

        var desc64 = new ActionDescriptor();

        var idPstn = charIDToTypeID( "Pstn" );

        var idPxl = charIDToTypeID( "#Pxl" );

        desc64.putUnitDouble( idPstn, idPxl, y );

        var idOrnt = charIDToTypeID( "Ornt" );

        var idOrnt = charIDToTypeID( "Ornt" );

        var idHrzn = charIDToTypeID( "Hrzn" );

        desc64.putEnumerated( idOrnt, idOrnt, idHrzn );

    var idGd = charIDToTypeID( "Gd  " );

    desc63.putObject( idNw, idGd, desc64 );

executeAction( idMk, desc63, DialogModes.NO );

}


Well. Here is not a complex code that will give you everything (as I think) values from smartObjectMore.

You decide what to do with them. )

var r = new ActionReference(); 

r.putEnumerated( charIDToTypeID( "Lyr " ), charIDToTypeID( "Ordn" ), charIDToTypeID( "Trgt" ) ); 

 

var obj = executeActionGet(r).getObjectValue(stringIDToTypeID("smartObjectMore"))

with (obj)

    {

    var _tmp;

    var id         = getString(stringIDToTypeID("ID"));

    var placed     = getString(stringIDToTypeID("placed"));

    var pageNumber = getInteger(stringIDToTypeID("pageNumber"));

    var totalPages = getInteger(stringIDToTypeID("totalPages"));

    var crop       = getInteger(stringIDToTypeID("crop"));

    var frameCount    = getInteger(stringIDToTypeID("frameCount"));

    var antiAliasType = getInteger(stringIDToTypeID("antiAliasType"));

    var type          = getInteger(stringIDToTypeID("type"));

    _tmp = getObjectValue(stringIDToTypeID("frameStep"));

    var frameStep = new Object({

        numerator  : _tmp.getInteger(stringIDToTypeID("numerator")),

        denominator: _tmp.getInteger(stringIDToTypeID("denominator"))

        });

    _tmp = getObjectValue(stringIDToTypeID("duration"));

    var duration = new Object({

        numerator  : _tmp.getInteger(stringIDToTypeID("numerator")),

        denominator: _tmp.getInteger(stringIDToTypeID("denominator"))

        });

    _tmp = getList(stringIDToTypeID("transform"));

    var transform  = new Array();

    for (var i = 0; i < _tmp.count; i+=2) transform.push([_tmp.getDouble(i), _tmp.getDouble(i+1)]);

    _tmp = getList(stringIDToTypeID("nonAffineTransform"));

    var nonAffineTransform  = new Array();

    for (var i = 0; i < _tmp.count; i+=2) nonAffineTransform.push([_tmp.getDouble(i), _tmp.getDouble(i+1)]);

    _tmp = getObjectValue(stringIDToTypeID("warp"));

    var warp = new Object({

        warpStyle:            typeIDToStringID(_tmp.getEnumerationValue(stringIDToTypeID("warpStyle"))),

        warpValue:            _tmp.getDouble(stringIDToTypeID("warpValue")),

        warpPerspective:      _tmp.getDouble(stringIDToTypeID("warpPerspective")),

        warpPerspectiveOther: _tmp.getDouble(stringIDToTypeID("warpPerspective")),

        warpRotate:           typeIDToStringID(_tmp.getEnumerationValue(stringIDToTypeID("warpRotate"))),

        uOrder:               _tmp.getInteger(stringIDToTypeID("uOrder")),

        vOrder:               _tmp.getInteger(stringIDToTypeID("vOrder")),

        bounds: new Object({

            top   : _tmp.getObjectValue(stringIDToTypeID("bounds")).getUnitDoubleValue(stringIDToTypeID("top")),

            left  : _tmp.getObjectValue(stringIDToTypeID("bounds")).getUnitDoubleValue(stringIDToTypeID("left")),

            bottom: _tmp.getObjectValue(stringIDToTypeID("bounds")).getUnitDoubleValue(stringIDToTypeID("bottom")),

            right : _tmp.getObjectValue(stringIDToTypeID("bounds")).getUnitDoubleValue(stringIDToTypeID("right")),

            }),

        });

    warp.meshPoints = new Array();

    try { // may be absent

        _tmp = _tmp.getObjectValue(stringIDToTypeID("customEnvelopeWarp")).getList(stringIDToTypeID("meshPoints"));

        for (var i = 0; i < _tmp.count; i++)

            {

            var x = _tmp.getObjectValue(i);

            warp.meshPoints.push([x.getUnitDoubleValue(stringIDToTypeID("horizontal")), x.getUnitDoubleValue(stringIDToTypeID("vertical"))]);

            }

        }

    catch(e) {}

    _tmp = getObjectValue(stringIDToTypeID("size"));

    var size = new Object({

        width:  _tmp.getDouble(stringIDToTypeID("width")),

        height: _tmp.getDouble(stringIDToTypeID("height")),

        });

    var resolution = getUnitDoubleValue(stringIDToTypeID("resolution"));   

    var comp = getInteger(stringIDToTypeID("comp"));   

    _tmp = getObjectValue(stringIDToTypeID("compInfo"));

    var compID = _tmp.getInteger(stringIDToTypeID("compID"));

    var originalCompID = _tmp.getInteger(stringIDToTypeID("originalCompID"));

    try { // may be absent

       _tmp = _tmp.getList(stringIDToTypeID("compList"));

       

        var compList = new Array();

        for (var i = 0; i < _tmp.count; i++)

            {

            var x = _tmp.getObjectValue(i);

            x.getString (stringIDToTypeID("name"));

            x.getInteger(stringIDToTypeID("ID"));

            x.getString (stringIDToTypeID("comment"));

            compList.push(new Object({

                name:    x.getString (stringIDToTypeID("name")),

                ID:      x.getInteger(stringIDToTypeID("ID")),

                comment: x.getString (stringIDToTypeID("comment"))

                }));

            }

        }

    catch(e) {}

    }

// examples

alert (warp.toSource())

alert (size.toSource())

alert (resolution)