Skip to main content
Participant
August 26, 2022
Answered

When a get layerSet id, any way to get all children layers id by ActionManager?

  • August 26, 2022
  • 1 reply
  • 333 views

We can traverse all layers by following code in dfs order:

 

 

/**
 * @9397041 {LayerSet} layerSet
 */
function traverse(layerSet) {
    const layers = layerSet.layers;
    for (var layer, i = layers.length - 1; i >= 0; i--) {
        layer = layers[i];
        if (layer.typename === 'LayerSet') {
            traverse(layer);
        }
        $.writeln(layer.name);
    }
}

 

But using DOM api is very expensive, I want to use actiom manager api to implements dfs order traverse. For now, I can using following code to traverse all layers in linear order:

 

function traverseByAmInLinearOrder(callback) {
    var ref = new ActionReference();
    ref.putProperty(charIDToTypeID('Prpr'), charIDToTypeID('NmbL'));
    ref.putEnumerated(charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt'));
    var desc = executeActionGet(ref);
    var layerCount = desc.getInteger(charIDToTypeID('NmbL'));
    var i = 0;
    try {
        activeDocument.backgroundLayer;
    } catch (e) {
        i = 1;
    }
    for (i; i <= layerCount; i++) {
        var ref = new ActionReference();
        ref.putIndex(charIDToTypeID('Lyr '), i);
        var desc = executeActionGet(ref);
        var id = desc.getInteger(stringIDToTypeID('layerID'));
        if (desc.getInteger(stringIDToTypeID('layerKind')) !== 13) {
            callback(desc);
        }
    }
}

traverseByAmInLinearOrder(function (desc) {
    $.writeln(desc.getString(stringIDToTypeID('name')));
});

 

In order to traverse layers in dfs order by am, I need to find a method to get all child layers id of layerSet by am. Any bros have ideas?

This topic has been closed for replies.
Correct answer YuTengjing

I finally implemet it by following code:

function createLayersTree() {
    var ref = new ActionReference();
    ref.putProperty(charIDToTypeID('Prpr'), charIDToTypeID('NmbL'));
    ref.putEnumerated(charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt'));
    var desc = executeActionGet(ref);
    var layerCount = desc.getInteger(charIDToTypeID('NmbL'));
    var startIndex = app.activeDocument.layers[app.activeDocument.layers.length - 1]
        .isBackgroundLayer
        ? 0
        : 1;
    const root = { layerId: null, layers: [] };
    var currentIndex = layerCount;
    function recursive(node) {
        var layerSet;
        while (currentIndex >= startIndex) {
            var ref = new ActionReference();
            ref.putIndex(charIDToTypeID('Lyr '), currentIndex);
            currentIndex--;
            var desc = executeActionGet(ref);
            var id = desc.getInteger(stringIDToTypeID('layerID'));
            var layerKind = desc.getInteger(stringIDToTypeID('layerKind'));
            var layerName = desc.getString(s2t('name'));
            var isGroup = layerKind === 7;
            var isGroupEnd = layerKind === 13;
            if (!isGroup && !isGroupEnd) {
                node.layers.push({
                    layerId: id,
                    layers: [],
                    layerName: layerName
                });
            } else if (isGroup) {
                layerSet = {
                    layerId: id,
                    layers: [],
                    layerName: layerName
                };
                node.layers.push(layerSet);
                recursive(layerSet);
            } else {
                return;
            }
        }
    }
    recursive(root);
    return root;
}

$.writeln(JSON.stringify(createLayersTree(), null, 4));

 

1 reply

Legend
August 26, 2022

You are missing the hidden layer, which is the group separator:

 

desc.getInteger(stringIDToTypeID('layerKind')) !== 13

 

it is necessary to solve your problem in this situation: all groups have separators at the beginning and end. You can get information about them by getting the layerSection property for each layer. The start of the group is layerSectionStart, the end of the group is layerSectionEnd. That is, given the layer ID of the group, you can get the layer index and move on until you find the end of the section. Don not forget to take into account nested groups.

You can use this code as a base:

 

var doc = new AM('document'),
    lr = new AM('layer');

var layerSectionContent = getLayersCollection(lr.getProperty(('itemIndex'), lr.getProperty('layerID'))/*<- get active layer ID*/, true)
alert(layerSectionContent.toSource())

function getLayersCollection(idx, getLayerSectionContent) {
    var indexFrom = idx ? (doc.getProperty('hasBackgroundLayer') ? --idx : idx) : doc.getProperty('numberOfLayers'),
        indexTo = doc.getProperty('hasBackgroundLayer') ? 0 : 1;
    return enumLayers(indexFrom, indexTo, getLayerSectionContent);
    function enumLayers(from, to, currentSection, parentItem, group) {
        parentItem = parentItem ? parentItem : [];
        var isDirty = false;
        for (var i = from; i >= to; i--) {
            var layerSection = lr.getProperty('layerSection', i, true).value;
            if (layerSection == 'layerSectionStart') {
                i = enumLayers(i - 1, to, undefined, [], parentItem)
                isDirty = true;
                if (currentSection) return parentItem[0]
                continue;
            }
            if (currentSection && isDirty) return []
            var properties = {};
            //collect properties here
            properties.layerKind = lr.getProperty('name', i, true)
            properties.id = lr.getProperty('layerID', i, true)
            if (layerSection == 'layerSectionEnd') {
                for (o in properties) { parentItem[o] = properties[o] }
                group.push(parentItem);
                return i;
            } else {
                parentItem.push(properties)
                if (currentSection && !isDirty) return parentItem
            }
        }
        return parentItem
    }
}

function AM(target, order) {
    var s2t = stringIDToTypeID,
        t2s = typeIDToStringID;
    target = target ? s2t(target) : null;
    this.getProperty = function (property, id, idxMode) {
        property = s2t(property);
        (r = new ActionReference()).putProperty(s2t('property'), property);
        id != undefined ? (idxMode ? r.putIndex(target, id) : r.putIdentifier(target, id)) :
            r.putEnumerated(target, s2t('ordinal'), order ? s2t(order) : s2t('targetEnum'));
        return getDescValue(executeActionGet(r), property)
    }
    this.hasProperty = function (property, id, idxMode) {
        property = s2t(property);
        (r = new ActionReference()).putProperty(s2t('property'), property);
        id ? (idxMode ? r.putIndex(target, id) : r.putIdentifier(target, id))
            : r.putEnumerated(target, s2t('ordinal'), order ? s2t(order) : s2t('targetEnum'));
        return executeActionGet(r).hasKey(property)
    }
    function getDescValue(d, p) {
        switch (d.getType(p)) {
            case DescValueType.OBJECTTYPE: return { type: t2s(d.getObjectType(p)), value: d.getObjectValue(p) };
            case DescValueType.LISTTYPE: return d.getList(p);
            case DescValueType.REFERENCETYPE: return d.getReference(p);
            case DescValueType.BOOLEANTYPE: return d.getBoolean(p);
            case DescValueType.STRINGTYPE: return d.getString(p);
            case DescValueType.INTEGERTYPE: return d.getInteger(p);
            case DescValueType.LARGEINTEGERTYPE: return d.getLargeInteger(p);
            case DescValueType.DOUBLETYPE: return d.getDouble(p);
            case DescValueType.ALIASTYPE: return d.getPath(p);
            case DescValueType.CLASSTYPE: return d.getClass(p);
            case DescValueType.UNITDOUBLE: return (d.getUnitDoubleValue(p));
            case DescValueType.ENUMERATEDTYPE: return { type: t2s(d.getEnumerationType(p)), value: t2s(d.getEnumerationValue(p)) };
            default: break;
        };
    }
}

 

Participant
September 6, 2022

hidden layer(layerKind=13) is useless for most case, so I skip it.

good good study, day day up!
YuTengjingAuthorCorrect answer
Participant
September 6, 2022

I finally implemet it by following code:

function createLayersTree() {
    var ref = new ActionReference();
    ref.putProperty(charIDToTypeID('Prpr'), charIDToTypeID('NmbL'));
    ref.putEnumerated(charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt'));
    var desc = executeActionGet(ref);
    var layerCount = desc.getInteger(charIDToTypeID('NmbL'));
    var startIndex = app.activeDocument.layers[app.activeDocument.layers.length - 1]
        .isBackgroundLayer
        ? 0
        : 1;
    const root = { layerId: null, layers: [] };
    var currentIndex = layerCount;
    function recursive(node) {
        var layerSet;
        while (currentIndex >= startIndex) {
            var ref = new ActionReference();
            ref.putIndex(charIDToTypeID('Lyr '), currentIndex);
            currentIndex--;
            var desc = executeActionGet(ref);
            var id = desc.getInteger(stringIDToTypeID('layerID'));
            var layerKind = desc.getInteger(stringIDToTypeID('layerKind'));
            var layerName = desc.getString(s2t('name'));
            var isGroup = layerKind === 7;
            var isGroupEnd = layerKind === 13;
            if (!isGroup && !isGroupEnd) {
                node.layers.push({
                    layerId: id,
                    layers: [],
                    layerName: layerName
                });
            } else if (isGroup) {
                layerSet = {
                    layerId: id,
                    layers: [],
                    layerName: layerName
                };
                node.layers.push(layerSet);
                recursive(layerSet);
            } else {
                return;
            }
        }
    }
    recursive(root);
    return root;
}

$.writeln(JSON.stringify(createLayersTree(), null, 4));

 

good good study, day day up!