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.
Yes, change line :-
var Info = getNamesPlusIDs().sort();
to
var Info = getNamesPlusIDs();
then they should be in layer order.
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?
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.
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;
};
Copy link to clipboard
Copied
...that is exactly what i was looking for - thanks very much Paul
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)
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;
};
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?
Copy link to clipboard
Copied
Yes, change line :-
var Info = getNamesPlusIDs().sort();
to
var Info = getNamesPlusIDs();
then they should be in layer order.
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;
}
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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.
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],
};
return Names;
};
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.
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?
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?
Copy link to clipboard
Copied
Sorry, I have no idea. It works for me on Photoshop 2015 and 2017.
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>
Copy link to clipboard
Copied
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
Copy link to clipboard
Copied
can i use this script in illustrator?
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.