Artboard Dimenions

Hello!
I’m trying to create a Photoshop UXP extension that automatically adds the artboard’s dimensions to the artboard’s name. For example: Artboard1_1200x1200

I am currently using this code, that works, but it adds the total dimensions of the layers within the artboard, and not the artboard’s bounds.
Does anyone know what I am doing wrong?

 const addDimensions = document.getElementById("addDimensionsCheckbox").checked;
                if (addDimensions) {
                    const artboardWidth = artboard.bounds.right - artboard.bounds.left;
                    const artboardHeight = artboard.bounds.bottom - artboard.bounds.top;
                    newName += `_${artboardWidth}x${artboardHeight}`;
                }

I was able to make this work before using the basic Photoshop script, but that code doesn’t translate over to a UXP extension.

If anyone is interested, for that, I used this code:


// Function to get the dimensions of the artboard
function getArtboardDimensions(layer) {
  var ab = artboard_rectangle(layer);
  var aW = ab[2] - ab[0];
  var aH = ab[3] - ab[1];
  return aW + "x" + aH;
}

// Function to get the artboard rectangle
function artboard_rectangle(layer) {
  try {
    var r = new ActionReference();
    r.putProperty(stringIDToTypeID("property"), stringIDToTypeID("artboard"));
    if (layer) r.putIdentifier(stringIDToTypeID("layer"), layer.id);
    else r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
    var d = executeActionGet(r).getObjectValue(stringIDToTypeID("artboard")).getObjectValue(stringIDToTypeID("artboardRect"));
    var bounds = new Array();
    bounds[0] = d.getUnitDoubleValue(stringIDToTypeID("left"));
    bounds[1] = d.getUnitDoubleValue(stringIDToTypeID("top"));
    bounds[2] = d.getUnitDoubleValue(stringIDToTypeID("right"));
    bounds[3] = d.getUnitDoubleValue(stringIDToTypeID("bottom"));
    return bounds;
  } catch (e) {
    alert(e);
  }
}

I think artboard bounds are not shown in the way you are trying to get. Maybe try making a selection of ALL in the artboard and then get the selection width and height?

Thank you for the reply! My only issue with that, is if I have multiple artboards, I don’t think that would work. Do you happen to know of a sample code that I could try out? I am still new to this and having a hard time finding documentation.

I’m new to all this, but with the help of AI, I was able to get a little closer, it is now grabbing the document dimensions, rather than the artboard. Is there anything that you notice here that can be changed?

async function addSuffix() {
    const photoshop = require("photoshop");
    const app = photoshop.app;
    const batchPlay = photoshop.action.batchPlay;

    try {
        return await photoshop.core.executeAsModal(async () => {
            console.log("Starting addSuffix function");
            const activeDocument = app.activeDocument;
            console.log("Active document:", activeDocument.name);

            // Use batchPlay to get the document dimensions
            const docInfo = await batchPlay(
                [{
                    _obj: "get",
                    _target: [{
                        _ref: "document",
                        _property: "artboard",
                        _enum: "ordinal",
                        _value: "targetEnum"
                    }],
                    _options: {
                        dialogOptions: "dontDisplay"
                    }
                }], {
                    synchronousExecution: true
                }
            );

            // Log the entire document info for debugging
            console.log("Document info:", JSON.stringify(docInfo[0], null, 2));

            // Extract width and height from the document info
            const width = Math.round(docInfo[0].width._value);
            const height = Math.round(docInfo[0].height._value);
            console.log(`Document dimensions: ${width}x${height}`);

            // Get all layers in the document
            const layers = activeDocument.layers;
            console.log("Total layers:", layers.length);

            // Initialize variables for the artboard/group layer
            let artboardLayer = null;

            // Iterate through layers to find the artboard layer (group)
            for (let i = 0; i < layers.length; i++) {
                const layer = layers[i];
                console.log(`Layer ${i}: Name: ${layer.name}, Kind: ${layer.kind}`);

                // Check if the layer is a group (artboard)
                if (layer.kind === photoshop.constants.LayerKind.GROUP) {
                    artboardLayer = layer; // Found the artboard layer
                    console.log(`Found artboard: ${layer.name}`);
                    break; // Exit loop once we find the artboard
                }
            }

            // If no artboard was found, exit the function
            if (!artboardLayer) {
                console.log("No artboard layer found in the document.");
                return;
            }

            let newName = artboardLayer.name;

            // Adds dimensions to suffix
            const addDimensions = document.getElementById("addDimensionsCheckbox").checked;
            if (addDimensions) {
                newName += `_${width}x${height}`; // Use document dimensions
            }

            console.log(`New name for artboard: ${newName}`);

            // Attempt to rename the artboard/group layer
            artboardLayer.name = newName;
            console.log("Artboard renamed successfully");

        }, {
            commandName: "Rename Artboard",
        });
    } catch (error) {
        console.error("An error occurred in addSuffix:", error);
        // Display the error to the user
        app.showAlert(`An error occurred: ${error.message}`);
    }
}

Wanted to give this a bump to see if there is anyone that has suggestions.
I tried the BatchPlay method, but that somewhat worked, but only does it for 1 artboard. I would need it to do it for every artboard in the document (sometimes 20+).

Is it possible to have the UXP plugin run a JSX script?

Hi @eskaybee875,

if You want to rename all artboards, then You have to loop through all of them, get the dimensions of each and rename it.
This script also checks, if an artboard already has the dimensions in its name and will not rename it, if nothing changed.
I tried to comment the code as best as possible, hopefully it helps.


// Require everything we need to execute our function.
// Do this once outside Your function. You don't need to require
// everything again with each function call.
const photoshop = require("photoshop");
const { action: psAction, app: psApp, constants: psConstants } = photoshop;
const { batchPlay } = psAction;


// The delimiter for Your artboard names.
// We use this, to also replace old artboard dimensions, in case
// any of them get resized.
const DELIMITER = "_";

// A button to call the `rename` function.
const dimensionsButtonEl = document.getElementById("artboard-suffix-btn");

// If button exists, add a click listener with the `renameArtboards`
// callback function.
dimensionsButtonEl?.addEventListener("click", renameArtboards);

/** 
 * Returns the bounds of an artboard.
 *
 * @return {Object}
 */
const getArtboardRect = async (docID, artboardID) => {
  if (!docID || !artboardID) {
    throw new Error('Please provide a document ID and an ' + 
			'artboard ID');
  }
  
  // `batchPlay` requires an array, so it will return an array.
  // Store it in the `result` variable.
  const result = await batchPlay(
    [
      {
        _obj: "get",
        _target: [
          {
            _property: "artboard",
          },
          {
            _ref: "layer",
            _id: artboardID,
          },
          {
            _ref: "document",
            _id: docID,
          },
        ],
        _options: {
          dialogOptions: "dontDisplay",
        },
      },
    ],
    {}
  );
	
	// Return the artboard prop from the first array item of the
	// result.
  return result[0].artboard.artboardRect;
};


/**
 * Helper to escape regex.
 * @src: https://stackoverflow.com/a/3561711
 */
function escapeRegex(string) {
		return string.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&');
}

/**	
 * Gets dimensions of all artboards of the document
 * and appends them to their name in the layers panel.
 *
 * @return {void}
 */
const renameArtboards = async () => {
	try {
		await psCore.executeAsModal(async () => {
			// Get the active Document right away. 
			const activeDoc = psApp.activeDocument;
			
			// Get all artboards of the document.
			const artboards = activeDoc.artboards;
			
			// Loop through all artboards.
			for (let i = 0; i < artboards.length; i++) {
				const artboard = artboards[i];

				// Get bounds for this artboard.
				const artboardRect = await getArtboardRect(
					activeDoc.id, artboard.id
				);
				
				// Calculate width and height from bounds.
				const artboardW = artboardRect.right - artboardRect.left;
				const artboardH = artboardRect.bottom - artboardRect.top;
				
				// Get artboard name before renaming.
				const oldArtboardName = artboard.name;
				
				// Create a variable for the new name.
				let newName = oldArtboardName;
				
				// Some regex magic to get a sizes pattern, i.e. `_1920x1080`.
				// If we have a match, we know, the layer already has a suffix,
				// so we can replace it with the actual dimensions.
				// By escaping the delimiter, we should be able to use any 
				// character that we like.
				const p = escapeRegex(DELIMITER) + '\\d+\\s*x\\s*\\d+(?:\\s*x\\s*\\d+)?';
				const rE = new RegExp(p, 'g');
				
				// Extract last occurrence of old suffix from artboard
				// name and also remove `DELIMITER` from string.
				const oldSize = oldArtboardName
					?.match(rE)
					?.pop()
					.slice(DELIMITER.length);
				
				// Also remove old suffix from artboard name.
				if(oldSize) {
					newName = newName.slice(0, -(oldSize.length + DELIMITER.length))
				}
				
				// Build the new size string.
				const newSize = `${artboardW}x${artboardH}`
				
				// Size didn't change, don't need to rename anything,
				// so skip to next artboard.
				if(newSize === oldSize) {
					console.log(`Nothing changed for artboard '${newName}'.`)
					continue;
				};
				
				// Concatenate everything into a new string.
				artboard.name = `${newName}${DELIMITER}${newSize}`;
			}
		}, {	
			commandName: "Rename Artboard" 
		});
	} catch (e) {
		console.error("[addSuffix]:\n", e);
		// Display the error to the user
		// psApp.showAlert(`An error occurred: ${e.message}`);
	}
};

1 Like

You sir, are an absolute wizard! Thank you so much!
I had to do some minor tweaking (using AI), since this wasn’t working out of the box for me, but now it works flawlessly.
I believe the main change was just adding “core: psCore” to the const. I’m not sure why, but that seems to have fixed it. Here is my complete final code, all thanks to you!.

const photoshop = require("photoshop");
const {
  action: psAction,
  app: psApp,
  constants: psConstants,
  core: psCore,
} = photoshop;
const { batchPlay } = psAction;

const DELIMITER = "_";

const dimensionsButtonEl = document.getElementById("btnRename");

const getArtboardRect = async (docID, artboardID) => {
  if (!docID || !artboardID) {
    throw new Error("Please provide a document ID and an " + "artboard ID");
  }

  const result = await batchPlay(
    [
      {
        _obj: "get",
        _target: [
          {
            _property: "artboard",
          },
          {
            _ref: "layer",
            _id: artboardID,
          },
          {
            _ref: "document",
            _id: docID,
          },
        ],
        _options: {
          dialogOptions: "dontDisplay",
        },
      },
    ],
    {},
  );

  return result[0].artboard.artboardRect;
};

function escapeRegex(string) {
  return string.replace(/[/\-\\^$*+?.()|[\]{}]/g, "\\$&");
}

const renameArtboards = async () => {
  try {
    await psCore.executeAsModal(
      async () => {
        const activeDoc = psApp.activeDocument;

        const artboards = activeDoc.artboards;

        for (let i = 0; i < artboards.length; i++) {
          const artboard = artboards[i];

          const artboardRect = await getArtboardRect(activeDoc.id, artboard.id);

          const artboardW = artboardRect.right - artboardRect.left;
          const artboardH = artboardRect.bottom - artboardRect.top;

          const oldArtboardName = artboard.name;

          let newName = oldArtboardName;

          const p =
            escapeRegex(DELIMITER) + "\\d+\\s*x\\s*\\d+(?:\\s*x\\s*\\d+)?";
          const rE = new RegExp(p, "g");

          const oldSize = oldArtboardName
            ?.match(rE)
            ?.pop()
            .slice(DELIMITER.length);

          if (oldSize) {
            newName = newName.slice(0, -(oldSize.length + DELIMITER.length));
          }

          const newSize = `${artboardW}x${artboardH}`;

          if (newSize === oldSize) {
            console.log(`Nothing changed for artboard '${newName}'.`);
            continue;
          }

          artboard.name = `${newName}${DELIMITER}${newSize}`;
        }
      },
      {
        commandName: "Rename Artboard",
      },
    );
  } catch (e) {
    console.error("[addSuffix]:\n", e);
  }
};

dimensionsButtonEl?.addEventListener("click", renameArtboards);

You’re right @eskaybee875,

my fault, sorry.
I have a little different setup, so the error probably crept in, when I edited the code in the forum reply to match Your setup.
You can also remove the constants assignment, since it’s never being used (a leftover from my script setup).
psAction, psApp, psCore, etc. are basically just name aliases of the properties that belong to the required photoshop instance.
I prefer using aliases, because it makes clear to me, what core for example I’m jusing in my script.
You could also just stick to declaring the photoshop constants like this:

const { action, app, constants, core } = photoshop;

and use them in Your script.
So instead of writing

photoshop.core.doSomething()

each and eveytime again, You just need to write

core.doSomething()

Just log photoshop to the console and You’ll see, what properties exist.
The documentation is also very helpful.
Also, if I may suggest, read a few javascript basics (MDN as a great documention) to better understand what You’re doing.
AI is not always very helpful to understand the basics, but that’s of course just my opinion.

You probably should do some more edge case testing (I didn’t do that very extensively), like what if a layer name already has some weird characters or the name ends with sth like artboard_nr°666_1200x300x340?
Maybe You want another delimiter, like a double underscore __, an emoji 💩 or whatever. I know, it’s probably unlikely, but You never know! =)

I’d also recommend, to keep the comments in Your code, because its much easier to understand what happens if You come back in a few months and need to tweak it again.

Anyway, I’m not sure, if my solution is the most elegant – I feel like I have so much to learn – and I’m always happy about suggestions, too.

I’m glad that it works for You now.
Happy coding! =)

EDIT: I can’t edit my original reply anymore, so here is the corrected code:

// Require everything we need to execute our function.
// Do this once outside Your function. You don't need to require
// everything again with each function call.
const photoshop = require("photoshop");
const { core: psCore, action: psAction, app: psApp } = photoshop;
const { batchPlay } = psAction;


// The delimiter for Your artboard names.
// We use this, to also replace old artboard dimensions, in case
// any of them get resized.
const DELIMITER = "_";

// A button to call the `rename` function.
const dimensionsButtonEl = document.getElementById("artboard-suffix-btn");

// If button exists, add a click listener with the `renameArtboards`
// callback function.
dimensionsButtonEl?.addEventListener("click", renameArtboards);

/** 
 * Returns the bounds of an artboard.
 *
 * @return {Object}
 */
const getArtboardRect = async (docID, artboardID) => {
  if (!docID || !artboardID) {
    throw new Error('Please provide a document ID and an ' + 
			'artboard ID');
  }
  
  // `batchPlay` requires an array, so it will return an array.
  // Store it in the `result` variable.
  const result = await batchPlay(
    [
      {
        _obj: "get",
        _target: [
          {
            _property: "artboard",
          },
          {
            _ref: "layer",
            _id: artboardID,
          },
          {
            _ref: "document",
            _id: docID,
          },
        ],
        _options: {
          dialogOptions: "dontDisplay",
        },
      },
    ],
    {}
  );
	
	// Return the artboard prop from the first array item of the
	// result.
  return result[0].artboard.artboardRect;
};


/**
 * Helper to escape regex.
 * @src: https://stackoverflow.com/a/3561711
 */
function escapeRegex(string) {
		return string.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&');
}

/**	
 * Gets dimensions of all artboards of the document
 * and appends them to their name in the layers panel.
 *
 * @return {void}
 */
const renameArtboards = async () => {
	try {
		await psCore.executeAsModal(async () => {
			// Get the active Document right away. 
			const activeDoc = psApp.activeDocument;
			
			// Get all artboards of the document.
			const artboards = activeDoc.artboards;
			
			// Loop through all artboards.
			for (let i = 0; i < artboards.length; i++) {
				const artboard = artboards[i];

				// Get bounds for this artboard.
				const artboardRect = await getArtboardRect(
					activeDoc.id, artboard.id
				);
				
				// Calculate width and height from bounds.
				const artboardW = artboardRect.right - artboardRect.left;
				const artboardH = artboardRect.bottom - artboardRect.top;
				
				// Get artboard name before renaming.
				const oldArtboardName = artboard.name;
				
				// Create a variable for the new name.
				let newName = oldArtboardName;
				
				// Some regex magic to get a sizes pattern, i.e. `_1920x1080`.
				// If we have a match, we know, the layer already has a suffix,
				// so we can replace it with the actual dimensions.
				// By escaping the delimiter, we should be able to use any 
				// character that we like.
				const p = escapeRegex(DELIMITER) + '\\d+\\s*x\\s*\\d+(?:\\s*x\\s*\\d+)?';
				const rE = new RegExp(p, 'g');
				
				// Extract last occurrence of old suffix from artboard
				// name and also remove `DELIMITER` from string.
				const oldSize = oldArtboardName
					?.match(rE)
					?.pop()
					.slice(DELIMITER.length);
				
				// Also remove old suffix from artboard name.
				if(oldSize) {
					newName = newName.slice(0, -(oldSize.length + DELIMITER.length))
				}
				
				// Build the new size string.
				const newSize = `${artboardW}x${artboardH}`
				
				// Size didn't change, don't need to rename anything,
				// so skip to next artboard.
				if(newSize === oldSize) {
					console.log(`Nothing changed for artboard '${newName}'.`)
					continue;
				};
				
				// Concatenate everything into a new string.
				artboard.name = `${newName}${DELIMITER}${newSize}`;
			}
		}, {	
			commandName: "Rename Artboard" 
		});
	} catch (e) {
		console.error("[addSuffix]:\n", e);
		// Display the error to the user
		// psApp.showAlert(`An error occurred: ${e.message}`);
	}
};