Skip to main content

Harvesting positional information from Photoshop

Front-end Development

After accepting a Photoshop comp of a web page from a designer, one of my early tasks is to figure out the actual coordinates of elements on the page. I often need to do many measurements to determine element margins, paddings, and positions. This usually ends up being error-prone, and resulting in a lot of tweaks and back-and-forth discussion in the late stages of the process.

To assist with this process, I investigated Photoshop's scripting capabilities. I had done a little Photoshop scripting way back when, and was dreading figuring out the AppleScript syntax for what I need. Lo and behold (and old news to most people): Adobe has migrated to JavaScript as their primary scripting language some time ago. Huzzah!

I want to iterate through all layers in the document and save out their bounding coordinates to a file. First, that means figuring out how to get at the layers. Photoshop has its own custom DOM, and the top of the hierarchy is app (as opposed to window in a browser).

var artLayers = [];
function gatherArtLayers(layerSet) {
  for (var i = 0; i < layerSet.artLayers.length; i++) {
    artLayers.push(layerSet.artLayers[i]);
  }
  for (i = 0; i < layerSet.layerSets.length; i++) {
    gatherArtLayers(layerSet.layerSets[i]);
  }
}
gatherArtLayers(app.activeDocument);

This little function iterates through all the top-level layers and adds them to the artLayers array. It also iterates though the layer sets at the top level, and recursively calls the function on those. This effectively collapses the nested layer hierarchy into a flat list.

For each layer in the list, we'll want to pull out the position information and create a textual representation of it.

function processLayer(layer) {
  var output = '';
  
  output += layer.name + "\n";
  output += layer.kind + "\n";
  output += layer.bounds[0].as('px') + "\n";
  output += layer.bounds[1].as('px') + "\n";
  output += layer.bounds[2].as('px') + "\n";
  output += layer.bounds[3].as('px') + "\n";
  if (layer.kind == LayerKind.TEXT) {
    var text = layer.textItem;
    output += text.contents + "\n";
    try {
      output += text.font + "\n";
      output += text.size.value + text.size.type + "\n";
    }
    catch(error) {
    }
  }
  
  return output;
}

When this function is called on a layer, we are pulling out its .name and .kind properties, as well as its bounds information (the top, left, bottom, and right coordinates of the layer). So that we don't rely on the settings of the file, we make sure to report the bounds info in pixels. If it's a text layer, we also want to harvest the text contents, font face, and font size.

What's up with the try/catch block, you may ask? Well, it turns out that some PSD files (notably ones exported from Illustrator) are malformed in some way, causing properties that should exist not to. JavaScript thinks that font is a property of text when you ask, but actually retrieving the property throws an error. This is a workaround for the moment so that the rest of the script can still run.

Finally, we need to call this function on each layer and save the output. Photoshop gives us a File object that provides a method to prompt for a save dialog, which is perfect for this.

var outputFile = File.saveDialog();
outputFile.open('w');

for (var i = 0; i < artLayers.length; i++) {
  outputFile.writeln(processLayer(artLayers[i]));
}

outputFile.close();

That's all we need for some basic data harvesting! Next up, I'll be looking into how we can make this output just a bit more useful.

Need a fresh perspective on a tough project?

Let’s talk about how RDG can help.

Contact Us