Export co-ordinates/layer bounds

Community Beginner ,
Sep 11, 2012 Sep 11, 2012

Copy link to clipboard

Copied

I'm looking for a simple script that will export the coordinates (layer bounds - top left x,y) of the layers to an seperate xml/txt file. I've google searched and the examples i find give far to much info layer height, width, centre, transform point, stack etc which I then have to waste time editing out.

Thanks in advance.

TOPICS
Actions and scripting

Views

9.4K

Likes

Translate

Translate

Report

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

correct answers 1 Correct Answer

Valorous Hero , Sep 20, 2012 Sep 20, 2012
Yes, change line :-var Info = getNamesPlusIDs().sort();tovar Info = getNamesPlusIDs();then they should be in layer order.

Likes

Translate

Translate
Adobe Community Professional ,
Sep 11, 2012 Sep 11, 2012

Copy link to clipboard

Copied

If they have not been exported as binary you could just edit the recording or writing of that information out in the Scipt itself.

Could you provide a link to one of those?

Likes

Translate

Translate

Report

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 Beginner ,
Sep 11, 2012 Sep 11, 2012

Copy link to clipboard

Copied

This is the script I found that kind of gives me what I need, shame there's all the extra info included...

//

// This script exports extended layer.bounds information to [psd_file_name].xml

// by pattesdours

//

function docCheck() {

  // ensure that there is at least one document open

  if (!documents.length) {

  alert('There are no documents open.');

  return; // quit

  }

}

docCheck();

var originalRulerUnits = preferences.rulerUnits;

preferences.rulerUnits = Units.PIXELS;

var docRef = activeDocument;

var docWidth = docRef.width.value;

var docHeight = docRef.height.value;

var mySourceFilePath = activeDocument.fullName.path + "/";

// Code to get layer index / descriptor

//

cTID = function(s) { return app.charIDToTypeID(s); };

sTID = function(s) { return app.stringIDToTypeID(s); };

function getLayerDescriptor (doc, layer) {

  var ref = new ActionReference();

  ref.putEnumerated(cTID("Lyr "), cTID("Ordn"), cTID("Trgt"));

  return executeActionGet(ref)

};

function getLayerID(doc, layer) {

  var d = getLayerDescriptor(doc, layer);

  return d.getInteger(cTID('LyrI'));

};

var stackorder = 0;

// function from Xbytor to traverse all layers

traverseLayers = function(doc, ftn, reverse) {

  function _traverse(doc, layers, ftn, reverse) {

  var ok = true;

  for (var i = 1; i <= layers.length && ok != false; i++) {

  var index = (reverse == true) ? layers.length-i : i - 1;

  var layer = layers[index];

  if (layer.typename == "LayerSet") {

  ok = _traverse(doc, layer.layers, ftn, reverse);

  } else {

  stackorder = stackorder + 1;

  ok = ftn(doc, layer, stackorder);

  }

  }

  return ok;

  };

  return _traverse(doc, doc.layers, ftn, reverse);

};

// create a string to hold the data

var str ="";

// class using a contructor

function cLayer(doc, layer) {

//this.layerID = Stdlib.getLayerID(doc, layer);

this.layerID = getLayerID(doc, layer);

  //alert("layer ID: " + this.layerID);

this.layerWidth = layer.bounds[2].value - layer.bounds[0].value;

  this.layerHeight = layer.bounds[3].value - layer.bounds[1].value;

 

// these return object coordinates relative to canvas

  this.upperLeftX = layer.bounds[0].value;

  this.upperLeftY = layer.bounds[1].value;

  this.upperCenterX = this.layerWidth / 2 + layer.bounds[0].value;

  this.upperCenterY = layer.bounds[1].value;

  this.upperRightX = layer.bounds[2].value;

  this.upperRightY = layer.bounds[1].value;

  this.middleLeftX = layer.bounds[0].value;

  this.middleLeftY = this.layerHeight / 2 + layer.bounds[1].value;

  this.middleCenterX = this.layerWidth / 2 + layer.bounds[0].value;

  this.middleCenterY = this.layerHeight / 2 + layer.bounds[1].value;

  this.middleRightX = layer.bounds[2].value;

  this.middleRightY = this.layerHeight / 2 + layer.bounds[1].value;

  this.lowerLeftX = layer.bounds[0].value;

  this.lowerLeftY = layer.bounds[3].value;

  this.lowerCenterX = this.layerWidth / 2 + layer.bounds[0].value;

  this.lowerCenterY = layer.bounds[3].value;

  this.lowerRightX = layer.bounds[2].value;

  this.lowerRightY = layer.bounds[3].value;

// I'm adding these for easier editing of flash symbol transformation point (outputs a 'x, y' format)

// because I like to assign shortcut keys that use the numeric pad keyboard, like such:

// 7 8 9

// 4 5 6

// 1 2 3

//

this.leftBottom = this.lowerLeftX + ", " + this.lowerLeftY;

this.bottomCenter = this.lowerCenterX + ", " + this.lowerCenterY;

this.rightBottom = this.lowerRightX + ", " + this.lowerRightY;

this.leftCenter = this.middleLeftX + ", " + this.middleLeftY;

this.center = this.middleCenterX + ", " + this.middleCenterY;

this.rightCenter = this.middleRightX + ", " + this.middleRightY;

this.leftTop = this.upperLeftX + ", " + this.upperLeftY;

this.topCenter = this.upperCenterX + ", " + this.upperCenterY;

this.rightTop = this.upperRightX + ", " + this.upperRightY;

// these return object coordinates relative to layer bounds

  this.relUpperLeftX = layer.bounds[1].value - layer.bounds[1].value;

  this.relUpperLeftY = layer.bounds[0].value - layer.bounds[0].value;

  this.relUpperCenterX = this.layerWidth / 2;

  this.relUpperCenterY = layer.bounds[0].value - layer.bounds[0].value;

  this.relUpperRightX = this.layerWidth;

  this.relUpperRightY = layer.bounds[0].value - layer.bounds[0].value;

  this.relMiddleLeftX = layer.bounds[1].value - layer.bounds[1].value;

  this.relMiddleLeftY = this.layerHeight / 2;

  this.relMiddleCenterX = this.layerWidth / 2;

  this.relMiddleCenterY = this.layerHeight / 2;

  this.relMiddleRightX = this.layerWidth;

this.relMiddleRightY = this.layerHeight / 2;

  this.relLowerLeftX = layer.bounds[1].value - layer.bounds[1].value;

  this.relLowerLeftY = this.layerHeight;

  this.relLowerCenterX = this.layerWidth / 2;

this.relLowerCenterY = this.layerHeight / 2;

  this.relLowerRightY = this.layerHeight;

  this.relLowerRightX = this.layerWidth;

  this.relLowerRightY = this.layerHeight;

 

  return this;

}

// add header line

str = "<psd filename=\"" + docRef.name + "\" path=\"" + mySourceFilePath + "\" width=\"" + docWidth + "\" height=\"" + docHeight + "\">\n";

// now a function to collect the data

function exportBounds(doc, layer, i) {

  var isVisible = layer.visible;

  var layerData = cLayer(doc, layer);

  if(isVisible){

// Layer object main coordinates relative to its active pixels

  var str2 = "\t<layer name=\"" + layer.name

+ "\" stack=\"" + (i - 1) // order in which layers are stacked, starting with zero for the bottom-most layer

+ "\" position=\"" + leftTop // this is the

+ "\" layerwidth=\"" + layerData.layerWidth

+ "\" layerheight=\"" + layerData.layerHeight

+ "\" transformpoint=\"" + "center" + "\">" // hard-coding 'center' as the default transformation point

+ layer.name + ".png" + "</layer>\n" // I have to put some content here otherwise sometimes tags are ignored

str += str2.toString();

  };

};

// call X's function using the one above

traverseLayers(app.activeDocument, exportBounds, true);

// Use this to export XML file to same directory where PSD file is located

  var mySourceFilePath = activeDocument.fullName.path + "/";

// create a reference to a file for output

  var csvFile = new File(mySourceFilePath.toString().match(/([^\.]+)/)[1] + app.activeDocument.name.match(/([^\.]+)/)[1] + ".xml");

// open the file, write the data, then close the file

csvFile.open('w');

csvFile.writeln(str + "</psd>");

csvFile.close();

preferences.rulerUnits = originalRulerUnits;

// Confirm that operation has completed

alert("Operation Complete!" + "\n" + "Layer coordinates were successfully exported to:" + "\n" + "\n" + mySourceFilePath.toString().match(/([^\.]+)/)[1] + app.activeDocument.name.match(/([^\.]+)/)[1] + ".xml");

I don't really want to have to go back into the xml and edit any results as there's usually 75+ items.

Likes

Translate

Translate

Report

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
Valorous Hero ,
Sep 11, 2012 Sep 11, 2012

Copy link to clipboard

Copied

This will write the layer name and x, y to a text file on the desktop.

#target photoshop

main();

function main(){

if(!documents.length) return;

var Info = getNamesPlusIDs().sort();

var Name = app.activeDocument.name.replace(/\.[^\.]+$/, '');

var file = new File(Folder.desktop + "/" + Name + ".txt");

file.open("w", "TEXT", "????");

$.os.search(/windows/i)  != -1 ? file.lineFeed = 'windows'  : file.lineFeed = 'macintosh';

for(var a in Info) file.writeln(Info.toString());

file.close();

}

function getNamesPlusIDs(){

   var ref = new ActionReference();

   ref.putEnumerated( charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') );

   var count = executeActionGet(ref).getInteger(charIDToTypeID('NmbL')) +1;

   var Names=[];

try{

    activeDocument.backgroundLayer;

var i = 0; }catch(e){ var i = 1; };

   for(i;i<count;i++){

       if(i == 0) continue;

        ref = new ActionReference();

        ref.putIndex( charIDToTypeID( 'Lyr ' ), i );

        var desc = executeActionGet(ref);

        var layerName = desc.getString(charIDToTypeID( 'Nm  ' ));

        var Id = desc.getInteger(stringIDToTypeID( 'layerID' ));

        if(layerName.match(/^<\/Layer group/) ) continue;

        var vMask = desc.getBoolean(stringIDToTypeID('hasVectorMask' ));

    try{

      var adjust = typeIDToStringID(desc.getList (stringIDToTypeID('adjustment')).getClass (0));

      if(vMask == true){

          adjust = false;

          var Shape = true;

          }

      }catch(e){var adjust = false; var Shape = false;}

        var layerType = typeIDToStringID(desc.getEnumerationValue( stringIDToTypeID( 'layerSection' )));

        var isLayerSet =( layerType == 'layerSectionContent') ? false:true;

        var Vis = desc.getBoolean(stringIDToTypeID( 'visible' ));

        var descBounds = executeActionGet(ref).getObjectValue(stringIDToTypeID( "bounds" ));

        var X = descBounds.getUnitDoubleValue(stringIDToTypeID('left'));

        var Y = descBounds.getUnitDoubleValue(stringIDToTypeID('top'));

        if(Vis && !isLayerSet && !adjust) Names.push([[layerName],,]);

   };

return Names;

};

Likes

Translate

Translate

Report

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 Beginner ,
Sep 11, 2012 Sep 11, 2012

Copy link to clipboard

Copied

...that is exactly what i was looking for - thanks very much Paul

Likes

Translate

Translate

Report

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 Beginner ,
Sep 11, 2012 Sep 11, 2012

Copy link to clipboard

Copied

BUT! Can I be really pedantic and ask for the results to be shown as:

Layer Name - x123, y456

(123 + 456 used for example only)

Likes

Translate

Translate

Report

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
Valorous Hero ,
Sep 11, 2012 Sep 11, 2012

Copy link to clipboard

Copied

Here you are...

#target photoshop

main();

function main(){

if(!documents.length) return;

var Info = getNamesPlusIDs().sort();

var Name = app.activeDocument.name.replace(/\.[^\.]+$/, '');

var file = new File(Folder.desktop + "/" + Name + ".txt");

file.open("w", "TEXT", "????");

$.os.search(/windows/i)  != -1 ? file.lineFeed = 'windows'  : file.lineFeed = 'macintosh';

for(var a in Info) file.writeln(Info[0] +"-x"+Info[1]+",y"+Info[2]);

file.close();

}

function getNamesPlusIDs(){

   var ref = new ActionReference();

   ref.putEnumerated( charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') );

   var count = executeActionGet(ref).getInteger(charIDToTypeID('NmbL')) +1;

   var Names=[];

try{

    activeDocument.backgroundLayer;

var i = 0; }catch(e){ var i = 1; };

   for(i;i<count;i++){

       if(i == 0) continue;

        ref = new ActionReference();

        ref.putIndex( charIDToTypeID( 'Lyr ' ), i );

        var desc = executeActionGet(ref);

        var layerName = desc.getString(charIDToTypeID( 'Nm  ' ));

        var Id = desc.getInteger(stringIDToTypeID( 'layerID' ));

        if(layerName.match(/^<\/Layer group/) ) continue;

        var vMask = desc.getBoolean(stringIDToTypeID('hasVectorMask' ));

    try{

      var adjust = typeIDToStringID(desc.getList (stringIDToTypeID('adjustment')).getClass (0));

      if(vMask == true){

          adjust = false;

          var Shape = true;

          }

      }catch(e){var adjust = false; var Shape = false;}

        var layerType = typeIDToStringID(desc.getEnumerationValue( stringIDToTypeID( 'layerSection' )));

        var isLayerSet =( layerType == 'layerSectionContent') ? false:true;

        var Vis = desc.getBoolean(stringIDToTypeID( 'visible' ));

        var descBounds = executeActionGet(ref).getObjectValue(stringIDToTypeID( "bounds" ));

        var X = descBounds.getUnitDoubleValue(stringIDToTypeID('left'));

        var Y = descBounds.getUnitDoubleValue(stringIDToTypeID('top'));

        if(Vis && !isLayerSet && !adjust) Names.push([[layerName],,]);

   };

return Names;

};


Likes

Translate

Translate

Report

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 Beginner ,
Sep 20, 2012 Sep 20, 2012

Copy link to clipboard

Copied

...one final question (promise!) is there a reason why the co-ords are shown on the txt file alphabetically and not as layer order?

Likes

Translate

Translate

Report

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
Valorous Hero ,
Sep 20, 2012 Sep 20, 2012

Copy link to clipboard

Copied

Yes, change line :-

var Info = getNamesPlusIDs().sort();

to

var Info = getNamesPlusIDs();

then they should be in layer order.

Likes

Translate

Translate

Report

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
New Here ,
Dec 10, 2015 Dec 10, 2015

Copy link to clipboard

Copied

Hi Paul,

Great Script. One additional question though. I'd like to include the rotation (transform) of my smart objects in the exported .txt file. I found a script that suppose to work, but I cannot get it to work (with to your script). Any thoughts or examples how to best make this work?

||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

function angleFromMatrix(yy, xy)

{

  var toDegs = 180/Math.PI;

  return Math.atan2(yy, xy) * toDegs - 90;

}

function getActiveLayerRotation()

{

  var ref = new ActionReference();

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

  var desc = executeActionGet(ref).getObjectValue(stringIDToTypeID('textKey'))

  if (desc.hasKey(stringIDToTypeID('transform')))

  {

  desc = desc.getObjectValue(stringIDToTypeID('transform'))

  var yy = desc.getDouble(stringIDToTypeID('yy'));

  var xy = desc.getDouble(stringIDToTypeID('xy'));

  return angleFromMatrix(yy, xy);

  }

  return 0;

}

||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

Likes

Translate

Translate

Report

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 ,
Dec 10, 2015 Dec 10, 2015

Copy link to clipboard

Copied

Paul left years ago the append you appeded to is three years old.  The function you posted is for text layers not other layer types.

JJMack

Likes

Translate

Translate

Report

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 Beginner ,
Jun 12, 2017 Jun 12, 2017

Copy link to clipboard

Copied

This script works, and I extended it to also export width and height of the layer in addition to the layer x,y coordinates (see below) .

The problem is that it does not work with groups, only with single layers. I have groups I am exporting with the asset generator, and I need to know at what position to place them in my app. I don't know how to change the script to work with groups. Can somebody help?

Here is my modified script that exports width and height:

#target photoshop

main();

function main(){

if(!documents.length) return;

var Info = getNamesPlusIDs().sort();

var Name = app.activeDocument.name.replace(/\.[^\.]+$/, '');

var path = app.activeDocument.path;

var file = new File(path + "/" + Name + "-coords.txt");

file.open("w", "TEXT", "????");

$.os.search(/windows/i)  != -1 ? file.lineFeed = 'windows'  : file.lineFeed = 'macintosh';

for(var a in Info) file.writeln(Info[0] +"---x"+Info[1]+",y"+Info[2] + ",w" + Info[3]+ ",h" + Info[4]);

file.close();

}

function getNamesPlusIDs(){

  var ref = new ActionReference();

  ref.putEnumerated( charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') );

  var count = executeActionGet(ref).getInteger(charIDToTypeID('NmbL')) +1;

  var Names=[];

try{

    activeDocument.backgroundLayer;

var i = 0; }catch(e){ var i = 1; };

  for(i;i<count;i++){

      if(i == 0) continue;

        ref = new ActionReference();

        ref.putIndex( charIDToTypeID( 'Lyr ' ), i );

        var desc = executeActionGet(ref);

        var layerName = desc.getString(charIDToTypeID( 'Nm  ' ));

        var Id = desc.getInteger(stringIDToTypeID( 'layerID' ));

        if(layerName.match(/^<\/Layer group/) ) continue;

        var vMask = desc.getBoolean(stringIDToTypeID('hasVectorMask' ));

    try{

      var adjust = typeIDToStringID(desc.getList (stringIDToTypeID('adjustment')).getClass (0));

      if(vMask == true){

          adjust = false;

          var Shape = true;

          }

      }catch(e){var adjust = false; var Shape = false;}

        var layerType = typeIDToStringID(desc.getEnumerationValue( stringIDToTypeID( 'layerSection' )));

        var isLayerSet =( layerType == 'layerSectionContent') ? false:true;

        var Vis = desc.getBoolean(stringIDToTypeID( 'visible' ));

        var descBounds = executeActionGet(ref).getObjectValue(stringIDToTypeID( "bounds" ));

        var X = descBounds.getUnitDoubleValue(stringIDToTypeID('left'));

        var Y = descBounds.getUnitDoubleValue(stringIDToTypeID('top'));

  var Wt = descBounds.getUnitDoubleValue(stringIDToTypeID('width'));

  var Ht = descBounds.getUnitDoubleValue(stringIDToTypeID('height'));

        if(Vis && !isLayerSet && !adjust) Names.push([[layerName],,, [Wt], [Ht]]);

  };

return Names;

};

Likes

Translate

Translate

Report

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 ,
Jun 12, 2017 Jun 12, 2017

Copy link to clipboard

Copied

You would need to recursively process all the layer in your document and write out a record for each layer noting the group it is in its layer name more then one layer can have the same name record each layer size and location in its record. Groups can be nested if you have nested groups the group name need to handle nesting of groups.

JJMack

Likes

Translate

Translate

Report

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 Beginner ,
Jun 12, 2017 Jun 12, 2017

Copy link to clipboard

Copied

Thank you. I was wondering if there's a simpler alternative... could we have a script that merges all (toplevel) groups separately?

Likes

Translate

Translate

Report

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
New Here ,
Jun 20, 2017 Jun 20, 2017

Copy link to clipboard

Copied

Hi seguso2, I've been trying to use your extended script, but I'm getting an Error 8800 at line 44. Can you recommend something I do to fix the error?

Likes

Translate

Translate

Report

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 Beginner ,
Jun 20, 2017 Jun 20, 2017

Copy link to clipboard

Copied

LATEST

Sorry, I have no idea. It works for me on Photoshop 2015 and 2017.

Likes

Translate

Translate

Report

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
New Here ,
Dec 13, 2012 Dec 13, 2012

Copy link to clipboard

Copied

Is there a way to create a XML file with folders included?

<psd>

<layer></layer>

<layer></layer>

<folder>

<layer></layer>

</folder

</psd>

Likes

Translate

Translate

Report

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
New Here ,
Sep 08, 2014 Sep 08, 2014

Copy link to clipboard

Copied

Capture.PNG

Script is working fine for image coordinates but When i try to extract the layer position i am not getting extract position from psd. can you help me getting correct data from psd. attached is the value expected'default text layer' as 43, 60.77 but i am getting different position. Advance thanks

Likes

Translate

Translate

Report

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
New Here ,
Apr 26, 2015 Apr 26, 2015

Copy link to clipboard

Copied

can i use this script in illustrator?

Likes

Translate

Translate

Report

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 ,
Jun 12, 2017 Jun 12, 2017

Copy link to clipboard

Copied

You could code that you just would not process groups that are nested in a group.  All layers in nested groups would simply not be processed. Layers outside of Groups and Layers in top level groups would be processed.

JJMack

Likes

Translate

Translate

Report

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