Copy link to clipboard
Copied
We can traverse all layers by following code in dfs order:
/**
* @Param {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?
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
...
Copy link to clipboard
Copied
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;
};
}
}
Copy link to clipboard
Copied
hidden layer(layerKind=13) is useless for most case, so I skip it.
Copy link to clipboard
Copied
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));