Skip to main content
Participant
March 22, 2024
Answered

Changing LayerSet type to ArtBoard with JavaScript

  • March 22, 2024
  • 3 replies
  • 1151 views

I'm trying to write a script for Photoshop where it will create artboards for each selected layer, move the layer to it, and then move the artboards to create margin gaps between them all.

 

I have a for loop iterating through the list of selected layers that is passed into this function:

// Function to create new artboard (LayerSet) for selected layer
// Unfortunately, this only creates a layer group and not an artboard
function createArtboard(layer) {
	
    var doc = layer.parent;

    // Create new artboard as a LayerSet
    var artboard = doc.layerSets.add();
    artboard.name = layer.name + "_Artboard";

    // Move the selected layer into the newly created artboard
    layer.move(artboard, ElementPlacement.INSIDE);
    
    // Set the bounds of the artboard LayerSet to match the layer bounds
    artboard.bounds = artboardBounds;
}

The problem, is that it's creating a basic LayerSet--a layer group--and then appending the layers to it. I have read that artboards are considered a type of LayerSet, however I have not found a way to create an artboard using JavaScript. I thought maybe if it is a type of LayerSet, you could change the type from Group to Artboard, and define the artboard's bounds as well.

 

I have found how to make an artboard using the ActionDescriptor, but this is beyond my comprehension and I cannot find a way to move layers to them using this. I get all the selected layers in my script using this, too, but I really don't understand how it works and I'd like to keep it readible for me, if possible.

 

Any help is appreciated!

This topic has been closed for replies.
Correct answer joshuah80472780

I had a couple issues with running your script, and I figured I should take the time to better understand extended script code and how it works. I think the method you use works for getting certain actions work, but I also wanted to make the code a little bit more readible (for me at least).

 

The version I have now creates artboards based on the width and height of the image, and the placement of the artboards is dependent on the width / 16 to create margins. It renames each artboard based on their respective layer, and then moves those layers to their artboards. It also changes the artboard background type to transparent.

 

I noticed that the action listener splits the action descriptors/references variables onto multiple lines, so I decided to trim it to fit onto more lines, since it makes more sense to me. However, I understand it may not be as readible for more experienced AM coders.

 

It only works with pixels as it is, I am pretty sure it shrinks the artboard down when using inches as the unit.

 

Additionally, I found that in UXP, you can create this same thing as plugin where the coding is a little bit more simplified, where it can autocomplete some things. It still takes time to get used to, but might be a little easier to get into then AM code.

Thank you so much for the help!

 

Either way, here is the code I have:

// Create new artboard
function createArtboard(layer, i, dimensions) {

    // Variables for artboard size and placement
    const WIDTH = dimensions[0];
    const HEIGHT = dimensions[1];
    const MARGIN = WIDTH / 16; // Margin is 1/16 of document's width
    const start = (WIDTH + MARGIN) * i; // start placement of artboard

    // Create artboard descriptor
    var desc = new ActionDescriptor();
	var ref = new ActionReference();
    ref.putClass(stringIDToTypeID("artboardSection"));
	desc.putReference( charIDToTypeID('null'), ref );

    // Artboard Rect bounds
    var artboardRectDesc = new ActionDescriptor();
    artboardRectDesc.putUnitDouble(charIDToTypeID("Top "), charIDToTypeID("#Pxl"), 0);
    artboardRectDesc.putUnitDouble(charIDToTypeID("Left"), charIDToTypeID("#Pxl"), start);
    artboardRectDesc.putUnitDouble(charIDToTypeID("Btom"), charIDToTypeID("#Pxl"), HEIGHT);
    artboardRectDesc.putUnitDouble(charIDToTypeID("Rght"), charIDToTypeID("#Pxl"), start + WIDTH);
    desc.putObject(stringIDToTypeID("artboardRect"), stringIDToTypeID("rectangle"), artboardRectDesc);

    // Execute action to create artboard
    executeAction(stringIDToTypeID("make"), desc, DialogModes.NO);
	
    // Move the selected layer into the newly created, renamed artboard
    var artboard = app.activeDocument.activeLayer;
    artboard.name = layer.name; // rename
    layer.move(artboard, ElementPlacement.INSIDE);

    // Changes artboard BG type
    changeBackground(artboard);
}

function changeBackground(layer) {
    
    // Command on selected artboard
    // Artboard is selected already

    // Transparent BG type
    const BGTYPE = 3;

    // Create edit artboard descriptors
    var desc_editBoard = new ActionDescriptor();
    var ref_layer = new ActionReference();
    ref_layer.putEnumerated( stringIDToTypeID("layer"), stringIDToTypeID( "ordinal" ), stringIDToTypeID( "targetEnum" ) );
    desc_editBoard.putReference( charIDToTypeID('null'), ref_layer );

    // Artboard BG type descriptors
    var artboardBGTypeDesc = new ActionDescriptor();
    artboardBGTypeDesc.putInteger( stringIDToTypeID( "artboardBackgroundType" ), BGTYPE ); // Adds BGTYPE 
    var idartboard = stringIDToTypeID( "artboard" );
    desc_editBoard.putObject( idartboard, idartboard, artboardBGTypeDesc );
    desc_editBoard.putInteger( stringIDToTypeID( "changeBackground" ), 1 ); // Changes BG

    // Execute action to change BG type
    executeAction( stringIDToTypeID( "editArtboardEvent" ), desc_editBoard, DialogModes.NO );

}

// Gets selected layers
// Code initially by Paul Riggott
function GetSelectedLayers() {
    var A = [];
    var ref = new ActionReference();
    ref.putEnumerated(charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt'));
    var desc = executeActionGet(ref);
    if (desc.hasKey(stringIDToTypeID('targetLayers'))) {
        desc = desc.getList(stringIDToTypeID('targetLayers'));
        for (var i = 0; i < desc.count; i++) {
            var index = desc.getReference(i).getIndex();
            A.push(app.activeDocument.layers[index]);
        }
    }
    return A;
};

// Main function
function main() {
    const doc = app.activeDocument;
    const docDimensions = [doc.width, doc.height];

    var selectedLayers = GetSelectedLayers();

    // Loop through selected layers to create artboards
    for (var i = 0; i < selectedLayers.length; i++) {
        var layer = selectedLayers[i];
        createArtboard(layer, i, docDimensions);
    }
}

// Run the main function
main();

 

3 replies

Stephen Marsh
Community Expert
Community Expert
March 23, 2024

@joshuah80472780 

 

The following script will create an artboard at the single selected layer's bounds with a transparent background. This would need to be run multiple times, manually selecting one layer at a time.

 

It does not align or distribute the artboards, that will be up to you. Please share the code for aligning and or distributing a set distance between the artboards.

 

I'll post updated code to process all selected layers as individual artboards.

 

/*
Create Artboard From Single Selected Layer.jsx
v1.0 - 23rd March 2024, Stephen Marsh
https://community.adobe.com/t5/photoshop-ecosystem-discussions/changing-layerset-type-to-artboard-with-javascript/td-p/14508167
*/

var origDocName = app.activeDocument.name;
var origLayer = app.activeDocument.activeLayer;
var origLayerName = app.activeDocument.activeLayer.name;

makeABfromLayer();


///// FUNCTIONS /////

function makeABfromLayer() {
    dupeLayerToTempDoc(origLayerName + " Temp");
    layerToArtboard(origLayerName + " Artboard");
    artBoardBackground(3);
    dupeArtboardToOrigDoc();
    app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
    origLayer.remove();
    app.activeDocument.activeLayer.layers[0].name = origLayerName;
}

function dupeLayerToTempDoc(docName) {
    var s2t = function (s) {
        return app.stringIDToTypeID(s);
    };
    var descriptor = new ActionDescriptor();
    var reference = new ActionReference();
    var reference2 = new ActionReference();
    reference.putClass(s2t("document"));
    descriptor.putReference(s2t("null"), reference);
    descriptor.putString(s2t("name"), docName);
    reference2.putEnumerated(s2t("layer"), s2t("ordinal"), s2t("targetEnum"));
    descriptor.putReference(s2t("using"), reference2);
    executeAction(s2t("make"), descriptor, DialogModes.NO);
}

function layerToArtboard(artboardName) {
    var idmake = stringIDToTypeID("make");
    var desc2395 = new ActionDescriptor();
    var idnull = stringIDToTypeID("null");
    var ref446 = new ActionReference();
    var idartboardSection = stringIDToTypeID("artboardSection");
    ref446.putClass(idartboardSection);
    desc2395.putReference(idnull, ref446);
    var idfrom = stringIDToTypeID("from");
    var ref447 = new ActionReference();
    var idlayer = stringIDToTypeID("layer");
    var idordinal = stringIDToTypeID("ordinal");
    var idtargetEnum = stringIDToTypeID("targetEnum");
    ref447.putEnumerated(idlayer, idordinal, idtargetEnum);
    desc2395.putReference(idfrom, ref447);
    var idusing = stringIDToTypeID("using");
    var desc2396 = new ActionDescriptor();
    var idname = stringIDToTypeID("name");
    desc2396.putString(idname, artboardName);
    var idartboardSection = stringIDToTypeID("artboardSection");
    desc2395.putObject(idusing, idartboardSection, desc2396);
    var idname = stringIDToTypeID("name");
    desc2395.putString(idname, artboardName);
    executeAction(idmake, desc2395, DialogModes.NO);
}

function artBoardBackground(artBoardColor) {
    /* https://community.adobe.com/t5/photoshop-ecosystem-discussions/setting-artboard-background-to-transparent/m-p/12438157 */
    var ideditArtboardEvent = stringIDToTypeID("editArtboardEvent");
    var desc520 = new ActionDescriptor();
    var idnull = stringIDToTypeID("null");
    var ref129 = new ActionReference();
    var idlayer = stringIDToTypeID("layer");
    var idordinal = stringIDToTypeID("ordinal");
    var idtargetEnum = stringIDToTypeID("targetEnum");
    var desc521 = new ActionDescriptor();
    var idartboard = stringIDToTypeID("artboard");
    var idchangeBackground = stringIDToTypeID("changeBackground");
    ref129.putEnumerated(idlayer, idordinal, idtargetEnum);
    desc520.putReference(idnull, ref129);
    // Static options for "Other" idartboardBackgroundType = 4
    var idcolor = stringIDToTypeID("color");
    var desc523 = new ActionDescriptor();
    var idred = stringIDToTypeID("red");
    desc523.putDouble(idred, 128.000000);
    var idgrain = stringIDToTypeID("grain");
    desc523.putDouble(idgrain, 128.000000);
    var idblue = stringIDToTypeID("blue");
    desc523.putDouble(idblue, 128.000000);
    var idRGBColor = stringIDToTypeID("RGBColor");
    desc521.putObject(idcolor, idRGBColor, desc523);
    var idartboardBackgroundType = stringIDToTypeID("artboardBackgroundType");
    // putInteger 1 = White, 2 = Black, 3 = Transparent, 4 = Other
    desc521.putInteger(idartboardBackgroundType, artBoardColor);
    desc520.putObject(idartboard, idartboard, desc521);
    desc520.putInteger(idchangeBackground, 1);
    executeAction(ideditArtboardEvent, desc520, DialogModes.NO);
}

function dupeArtboardToOrigDoc() {
    function s2t(s) {
        return app.stringIDToTypeID(s);
    }
    var descriptor = new ActionDescriptor();
    var reference = new ActionReference();
    var reference2 = new ActionReference();
    reference.putEnumerated(s2t("layer"), s2t("ordinal"), s2t("targetEnum"));
    descriptor.putReference(s2t("null"), reference);
    reference2.putName(s2t("document"), origDocName); // target doc name
    descriptor.putReference(s2t("to"), reference2);
    executeAction(s2t("duplicate"), descriptor, DialogModes.NO);
}

 

Stephen Marsh
Community Expert
Community Expert
March 23, 2024

I'm having some issues with adapting the script to run on multiple selected layers... So I'm going to cheat!

 

Record the execution of the previous script into an action.

 

Then use the following code from @jazz-y  to apply the selected action to multiple selected layers:

 

//Jazz-y
// Run currently selected action on selected layers.jsx

#target photoshop
var s2t = stringIDToTypeID;


(r = new ActionReference()).putProperty(s2t('property'), p = s2t('targetLayersIDs'));
r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
var lrs = executeActionGet(r).getList(p);

(r = new ActionReference()).putEnumerated(s2t('action'), s2t('ordinal'), s2t('targetEnum'));
try {
    try {
        var atn = executeActionGet(r).getString(s2t('name')),
            set = executeActionGet(r).getString(s2t('parentName'));
    }
    catch (e) { throw 'Before start select any action from actions palette!' }

    for (var i = 0; i < lrs.count; i++) {
        (r = new ActionReference()).putIdentifier(s2t('layer'), lrs.getReference(i).getIdentifier(s2t('layerID')));
        (d = new ActionDescriptor()).putReference(s2t('target'), r);
        try { executeAction(s2t('select'), d, DialogModes.NO); } catch (e) { throw e + '\nCannot select layer!' }
        (r = new ActionReference()).putName(s2t('action'), atn);
        r.putName(s2t('actionSet'), set);
        (d = new ActionDescriptor()).putReference(s2t('target'), r);
        try { executeAction(s2t('play'), d) } catch (e) { throw e + '\nCannot play action "' + atn + '" from set "' + set + '"' }
    }
} catch (e) { alert(e) }

 

Alternative scripts:

 

joshuah80472780AuthorCorrect answer
Participant
March 25, 2024

I had a couple issues with running your script, and I figured I should take the time to better understand extended script code and how it works. I think the method you use works for getting certain actions work, but I also wanted to make the code a little bit more readible (for me at least).

 

The version I have now creates artboards based on the width and height of the image, and the placement of the artboards is dependent on the width / 16 to create margins. It renames each artboard based on their respective layer, and then moves those layers to their artboards. It also changes the artboard background type to transparent.

 

I noticed that the action listener splits the action descriptors/references variables onto multiple lines, so I decided to trim it to fit onto more lines, since it makes more sense to me. However, I understand it may not be as readible for more experienced AM coders.

 

It only works with pixels as it is, I am pretty sure it shrinks the artboard down when using inches as the unit.

 

Additionally, I found that in UXP, you can create this same thing as plugin where the coding is a little bit more simplified, where it can autocomplete some things. It still takes time to get used to, but might be a little easier to get into then AM code.

Thank you so much for the help!

 

Either way, here is the code I have:

// Create new artboard
function createArtboard(layer, i, dimensions) {

    // Variables for artboard size and placement
    const WIDTH = dimensions[0];
    const HEIGHT = dimensions[1];
    const MARGIN = WIDTH / 16; // Margin is 1/16 of document's width
    const start = (WIDTH + MARGIN) * i; // start placement of artboard

    // Create artboard descriptor
    var desc = new ActionDescriptor();
	var ref = new ActionReference();
    ref.putClass(stringIDToTypeID("artboardSection"));
	desc.putReference( charIDToTypeID('null'), ref );

    // Artboard Rect bounds
    var artboardRectDesc = new ActionDescriptor();
    artboardRectDesc.putUnitDouble(charIDToTypeID("Top "), charIDToTypeID("#Pxl"), 0);
    artboardRectDesc.putUnitDouble(charIDToTypeID("Left"), charIDToTypeID("#Pxl"), start);
    artboardRectDesc.putUnitDouble(charIDToTypeID("Btom"), charIDToTypeID("#Pxl"), HEIGHT);
    artboardRectDesc.putUnitDouble(charIDToTypeID("Rght"), charIDToTypeID("#Pxl"), start + WIDTH);
    desc.putObject(stringIDToTypeID("artboardRect"), stringIDToTypeID("rectangle"), artboardRectDesc);

    // Execute action to create artboard
    executeAction(stringIDToTypeID("make"), desc, DialogModes.NO);
	
    // Move the selected layer into the newly created, renamed artboard
    var artboard = app.activeDocument.activeLayer;
    artboard.name = layer.name; // rename
    layer.move(artboard, ElementPlacement.INSIDE);

    // Changes artboard BG type
    changeBackground(artboard);
}

function changeBackground(layer) {
    
    // Command on selected artboard
    // Artboard is selected already

    // Transparent BG type
    const BGTYPE = 3;

    // Create edit artboard descriptors
    var desc_editBoard = new ActionDescriptor();
    var ref_layer = new ActionReference();
    ref_layer.putEnumerated( stringIDToTypeID("layer"), stringIDToTypeID( "ordinal" ), stringIDToTypeID( "targetEnum" ) );
    desc_editBoard.putReference( charIDToTypeID('null'), ref_layer );

    // Artboard BG type descriptors
    var artboardBGTypeDesc = new ActionDescriptor();
    artboardBGTypeDesc.putInteger( stringIDToTypeID( "artboardBackgroundType" ), BGTYPE ); // Adds BGTYPE 
    var idartboard = stringIDToTypeID( "artboard" );
    desc_editBoard.putObject( idartboard, idartboard, artboardBGTypeDesc );
    desc_editBoard.putInteger( stringIDToTypeID( "changeBackground" ), 1 ); // Changes BG

    // Execute action to change BG type
    executeAction( stringIDToTypeID( "editArtboardEvent" ), desc_editBoard, DialogModes.NO );

}

// Gets selected layers
// Code initially by Paul Riggott
function GetSelectedLayers() {
    var A = [];
    var ref = new ActionReference();
    ref.putEnumerated(charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt'));
    var desc = executeActionGet(ref);
    if (desc.hasKey(stringIDToTypeID('targetLayers'))) {
        desc = desc.getList(stringIDToTypeID('targetLayers'));
        for (var i = 0; i < desc.count; i++) {
            var index = desc.getReference(i).getIndex();
            A.push(app.activeDocument.layers[index]);
        }
    }
    return A;
};

// Main function
function main() {
    const doc = app.activeDocument;
    const docDimensions = [doc.width, doc.height];

    var selectedLayers = GetSelectedLayers();

    // Loop through selected layers to create artboards
    for (var i = 0; i < selectedLayers.length; i++) {
        var layer = selectedLayers[i];
        createArtboard(layer, i, docDimensions);
    }
}

// Run the main function
main();

 

Stephen Marsh
Community Expert
Community Expert
March 23, 2024

@joshuah80472780 – So if you had 3 layers selected, you would end up with 3 separate artboards, each containing a single layer?

 

Can you post a before/after screenshot of the layers panel to clarify?

 

Will there be layer groups or frame layers involved?

Participant
March 23, 2024

Before the script:

After running the script:

 

There wont be any layer groups or frame layers involved. You are correct, it would be 1:1 where the script would create an artboard for each layer.

Stephen Marsh
Community Expert
Community Expert
March 22, 2024

@joshuah80472780 

 

From memory, via the DOM, the only layer kind that can be changed is a normal layer into text layer (apart from changing a special Background image layer to a standard floating layer).

 

https://theiviaxx.github.io/photoshop-docs/Photoshop/ArtLayer/kind.html

 

I believe that you need to create the artboard, rather than attempting to convert to an artboard.

 

As artboards are not covered by the DOM you must use AM code.