[PS CC-CC2022] Автоматизация в Фотошопе

Layers - change shape stroke
Код:
/**Изменение толщины обводки (относительно исходного размера) всех слоев типа Shape документа
 * https://community.adobe.com/t5/photoshop-ecosystem-discussions/photoshop-script-to-iterate-through-all-layers-and-change-shape-stroke-size/td-p/12689665
 * https://youtu.be/7lk-rnzEkvY
 */
#target photoshop
var s2t = stringIDToTypeID,
    delta = 10;
(r = new ActionReference()).putProperty(s2t('property'), p = s2t('numberOfLayers'));
r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
var len = executeActionGet(r).getInteger(p),
    lrs = [];
for (var i = 1; i <= len; i++) {
    (r = new ActionReference()).putProperty(s2t('property'), p = s2t('layerKind'));
    r.putIndex(s2t('layer'), i);
    if (executeActionGet(r).getInteger(p) == 4) {
        (r = new ActionReference()).putProperty(s2t('property'), p = s2t('layerID'));
        r.putIndex(s2t('layer'), i);
        lrs.push(executeActionGet(r).getInteger(p));
    }
}
if (lrs.length) {
    var log = [];
    for (var i = 0; i < lrs.length; i++) {
        (r = new ActionReference()).putIdentifier(s2t('layer'), lrs[i]);
        (d = new ActionDescriptor()).putReference(s2t('null'), r);
        executeAction(s2t('select'), d, DialogModes.NO);
        (r = new ActionReference()).putProperty(s2t('property'), p = s2t('AGMStrokeStyleInfo'));
        r.putEnumerated(s2t('layer'), s2t('ordinal'), s2t('targetEnum'));
        try {
            var stroke = executeActionGet(r).getObjectValue(p);
            if (stroke.getBoolean(s2t('strokeEnabled'))) {
                var strokeSize = stroke.hasKey(p = s2t('strokeStyleLineWidth')) ? stroke.getUnitDoubleValue(p) : 0;
                (r = new ActionReference()).putEnumerated(s2t("contentLayer"), s2t("ordinal"), s2t("targetEnum"));
                (d = new ActionDescriptor()).putReference(s2t("null"), r);
                (d1 = new ActionDescriptor()).putUnitDouble(s2t("strokeStyleLineWidth"), s2t("pixelsUnit"), strokeSize + delta);
                (d2 = new ActionDescriptor()).putObject(s2t("strokeStyle"), s2t("strokeStyle"), d1);
                d.putObject(s2t("to"), s2t("shapeStyle"), d2);
                executeAction(s2t("set"), d, DialogModes.NO);
            }
        } catch (e) {
            // try to get layer info. remove this if not needed
            //==================================================
            (r = new ActionReference());
            r.putEnumerated(s2t('layer'), s2t('ordinal'), s2t('targetEnum'));
            (d = new ActionDescriptor()).putObject(s2t('object'), s2t('object'), executeActionGet(r));
            log.push(executeAction(s2t('convertJSONdescriptor'), d).getString(s2t('json')))
            //==================================================
        }
    } if (log.length) {
        logFile = File(Folder.desktop.fsName + '/' + 'layersLog.txt');
        logFile.open('a');
        logFile.writeln(log.join('\n'))
        logFile.close();
    }
}
 
Layers - check mask
Код:
/*Определить активен ли слой или его маска, переключить между ними
* https://community.adobe.com/t5/photoshop/detect-if-mask-or-layer-is-selected-with-js-script/m-p/11153249
https://youtu.be/-tVW4Kqsswc
*/
#target photoshop

s2t = stringIDToTypeID;

(r = new ActionReference()).putProperty(s2t('property'), p = s2t('hasUserMask'));
r.putEnumerated(s2t("layer"), s2t("ordinal"), s2t("targetEnum"));
if (executeActionGet(r).getBoolean(p)) {
    (r = new ActionReference()).putProperty(s2t('property'), p = s2t('name'));
    (r = new ActionReference()).putEnumerated(s2t("layer"), s2t("ordinal"), s2t("targetEnum"));
    layerName = executeActionGet(r).getString(p);

    (r = new ActionReference()).putProperty(s2t('property'), p = s2t('channelName'));
    r.putEnumerated(s2t("channel"), s2t("ordinal"), s2t("targetEnum"));
    channelName = executeActionGet(r).getString(p);

    (r = new ActionReference()).putProperty(s2t('property'), p = s2t('alphaChannelOptions'));
    r.putEnumerated(s2t("channel"), s2t("ordinal"), s2t("targetEnum"));
    alphaChannel = executeActionGet(r).hasKey(p)

    if (channelName.indexOf(layerName) == 0 && !alphaChannel) {
        var select = confirm('Layer mask selected\nSelect layer?') ? 'RGB' : null
    } else {
        var select = confirm('Layer selecter\nSelect mask?') ? 'mask' : null
    }
    if (select) {
        (r = new ActionReference()).putEnumerated(s2t("channel"), s2t("channel"), s2t(select));
        (d = new ActionDescriptor).putReference(s2t("null"), r);
        executeAction(s2t("select"), d, DialogModes.NO);
    }
}
 
Layers - collapse SO effects
Код:
/**Свернуть все эффекты смарт-объектов
 * https://community.adobe.com/t5/photoshop-ecosystem-discussions/action-or-script-to-close-smart-object-layer-styles-drawer/m-p/12537306#M600783
 * https://youtu.be/nv5HRxty5qU
 */
#target photoshop
s2t = stringIDToTypeID;
(r = new ActionReference()).putProperty(s2t('property'), p = s2t('numberOfDocuments'));
r.putEnumerated(s2t('application'), s2t('ordinal'), s2t('targetEnum'));
if (executeActionGet(r).getInteger(p)) {
    (r = new ActionReference()).putProperty(s2t('property'), p = s2t('targetLayersIDs'));
    r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
    var sel = executeActionGet(r).getList(p);
    (r = new ActionReference()).putProperty(s2t('property'), p = s2t('numberOfLayers'));
    r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
    var len = executeActionGet(r).getInteger(p);
    (r = new ActionReference()).putProperty(s2t('property'), p = s2t('hasBackgroundLayer'));
    r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
    var offset = executeActionGet(r).getInteger(p);
    (r = new ActionReference()).putIndex(s2t('layer'), 1 - offset);
    (d = new ActionDescriptor()).putReference(s2t('target'), r);
    executeAction(s2t('select'), d, DialogModes.NO);
    var lrs = { id: [], fx: [] };
    for (var i = 1 - offset; i <= len; i++) {
        (r = new ActionReference()).putProperty(s2t('property'), p = s2t('layerKind'));
        r.putIndex(s2t('layer'), i);
        var kind = executeActionGet(r).getInteger(p);
        if (kind == 5) {
            (r = new ActionReference()).putProperty(s2t('property'), p = s2t('layerID'));
            r.putIndex(s2t('layer'), i);
            lrs.id.push(executeActionGet(r).getInteger(p));
            (r = new ActionReference()).putProperty(s2t('property'), p = s2t('layerEffects'));
            r.putIndex(s2t('layer'), i);
            lrs.fx.push(executeActionGet(r).hasKey(p) ? executeActionGet(r).getObjectValue(p) : new ActionDescriptor());
            (d = new ActionDescriptor()).putReference(s2t('target'), r);
            (d1 = new ActionDescriptor()).putObject(s2t('outerGlow'), s2t('outerGlow'), new ActionDescriptor());
            d.putObject(s2t('to'), p, d1);
            executeAction(s2t('set'), d, DialogModes.NO);
        }
    }
    executeAction(s2t('collapseAllGroupsEvent'), new ActionDescriptor(), DialogModes.NO)
    for (var i = 0; i < lrs.id.length; i++) {
        if (lrs.fx[i].count) {
            (r = new ActionReference()).putIdentifier(s2t('layer'), lrs.id[i]);
            (d = new ActionDescriptor()).putReference(s2t('target'), r);
            executeAction(s2t('select'), d, DialogModes.NO);
            (r = new ActionReference()).putProperty(s2t('property'), p = s2t('layerEffects'));
            r.putIdentifier(s2t('layer'), lrs.id[i]);
            d.putObject(s2t('to'), p, lrs.fx[i]);
            executeAction(s2t('set'), d, DialogModes.NO);
        } else {
            (r = new ActionReference()).putIdentifier(s2t('layer'), lrs.id[i]);
            (d = new ActionDescriptor()).putReference(s2t('target'), r);
            executeAction(s2t('disableLayerFX'), d, DialogModes.NO);
        }
    }
    if (sel.count) {
        r = new ActionReference();
        for (var i = 0; i < sel.count; i++) { r.putIdentifier(s2t('layer'), sel.getReference(i).getIdentifier()) }
        (d = new ActionDescriptor()).putReference(s2t('target'), r);
        executeAction(s2t('select'), d, DialogModes.NO);
    }
}
 
Layers - convert SO exept smart filters
Код:
/**Растеризовать все смарт объекты, кроме тех которые имеют смарт-эффекты
 * https://community.adobe.com/t5/photoshop-ecosystem-discussions/rasterize-all-smart-objects-except-the-ones-with-smart-filters-script/td-p/12775611
 * https://youtu.be/tIgbQESzBaQ
 */
#target photoshop
s2t = stringIDToTypeID;
(r = new ActionReference()).putProperty(s2t('property'), p = s2t('numberOfLayers'));
r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
var len = executeActionGet(r).getInteger(p), lrs = [];
for (var i = 1; i <= len; i++) {
    (r = new ActionReference()).putProperty(s2t('property'), p = s2t('layerKind'));
    r.putIndex(s2t('layer'), i);
    if (executeActionGet(r).getInteger(p) == 5) {
        (r = new ActionReference()).putProperty(s2t('property'), p = s2t('smartObject'));
        r.putIndex(s2t('layer'), i);
        if (!executeActionGet(r).getObjectValue(p).hasKey(s2t('filterFX'))) lrs.push(i)
    }
}
if (lrs.length) {
    var r = new ActionReference()
    for (var i = 0; i < lrs.length; i++) r.putIndex(s2t('layer'), lrs[i]);
    (d = new ActionDescriptor()).putReference(s2t('target'), r);
    executeAction(s2t('select'), d, DialogModes.NO);
    (r = new ActionReference()).putEnumerated(s2t('layer'), s2t('ordinal'), s2t('targetEnum'));
    (d = new ActionDescriptor()).putReference(s2t('target'), r);
    executeAction(s2t('rasterizeLayer'), d, DialogModes.NO);
}
 
Layers - generate digits
Код:
/**Скрипт для фомирования числовых табло на основе шаблона
 * Переключает макеты цифр, сгруппиорованные по разрядам
 * Требования к макету:
 * - должны быть сформированы макеты цифр от 0 до 9.
 *   1 цифра - 1 слой (тип слоя не важен). Имя слоя - та же цифра
 * - цифры должны быть сгруппированы по разрядам
 *   (единицы - группа с именем 1, десятки - 10, сотни - 100 и т.п)
 * - перед началом работы все группы с разрядами должны быть выделены, а файл сохранен
https://community.adobe.com/t5/photoshop-ecosystem-discussions/how-to-write-this-script/td-p/12771527
https://youtu.be/KVg8aFLoIIM
*/
#target photoshop
var apl = new AM('application'),
    doc = new AM('document'),
    lr = new AM('layer');
if (apl.getProperty('numberOfDocuments')) {
    if (doc.getProperty('numberOfLayers')) {
        var targetLayers = doc.hasProperty('targetLayersIDs') ? doc.getProperty('targetLayersIDs') : [],
            pth = (doc.getProperty('fileReference')).path,
            selection = [];
        if (targetLayers) {
            for (var i = 0; i < targetLayers.count; i++) {
                var id = targetLayers.getReference(i).getIdentifier(stringIDToTypeID('layerID'))
                if (lr.getProperty('layerKind', id) == 7) selection.push(id)
            }
        }
        if (selection) {
            var digits = collectLayers(selection);
            for (var i = 123456; i <= 123480; i++) makeFile(i, digits, selection)
        }
    }
}
function collectLayers(s, o) {
    o = o ? o : {};
    for (var i = 0; i < s.length; i++) {
        o[Number(lr.getProperty('name', s[i]))] = getLayersList(s[i]);
    }
    return o;
    function getLayersList(id) {
        var idx = lr.getProperty('itemIndex', id),
            indexFrom = doc.getProperty('hasBackgroundLayer') ? --idx : idx,
            o = {};
        for (var i = indexFrom; i >= 1; i--) {
            var layerSection = lr.getProperty('layerSection', i, true).value
            if (layerSection == 'layerSectionStart') continue;
            if (layerSection == 'layerSectionEnd') break;
            o[Number(lr.getProperty('name', i, true))] = lr.getProperty('layerID', i, true);
        }
        return o
    }
}
function setDigits(o, digit) {
    var ids = []
    for (var a in o) ids.push(o[a])
    doc.visiblity(ids)
    doc.visiblity([o[digit]], true)
}
function makeFile(n, o, digits) {
    var fixed = '';
    for (var i = 0; i < digits.length; i++) fixed += '0'
    var num = (fixed + String(n)).substr(-digits.length),
        len = num.length - 1,
        div = 1;
    for (var i = len; i >= 0; i--) {
        var cur = Number(num.substr(i, 1))
        setDigits(o[div], cur)
        div = div * 10
    }
    doc.saveAsPDF(File(pth + '/' + num))
}
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)
    }
    this.descToObject = function (d) {
        var o = {}
        for (var i = 0; i < d.count; i++) {
            var k = d.getKey(i)
            o[t2s(k)] = getDescValue(d, k)
        }
        return o
    }
    this.selectLayerByIDList = function (IDList) {
        var ref = new ActionReference()
        for (var i = 0; i < IDList.length; i++) {
            ref.putIdentifier(s2t('layer'), IDList[i])
        }
        var desc = new ActionDescriptor()
        desc.putReference(s2t('target'), ref)
        desc.putBoolean(s2t('makeVisible'), false)
        executeAction(s2t('select'), desc, DialogModes.NO)
    }
    this.visiblity = function (ids, show) {
        var mode = show ? 'show' : 'hide',
            r = new ActionReference();
        do { r.putIdentifier(s2t('layer'), ids.shift()) } while (ids.length)
        (l = new ActionList()).putReference(r);
        (d = new ActionDescriptor()).putList(s2t("target"), l);
        executeAction(s2t(mode), d, DialogModes.NO);
    }
    this.saveAsPDF = function (fle) {
        (d = new ActionDescriptor()).putObject(s2t("as"), s2t("photoshopPDFFormat"), new ActionDescriptor());
        d.putPath(s2t("in"), fle);
        d.putBoolean(s2t("copy"), true);
        d.putBoolean(s2t("layers"), false);
        d.putEnumerated(s2t("saveStage"), s2t("saveStageType"), s2t("saveBegin"));
        executeAction(s2t("save"), d, DialogModes.NO);
    }
    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;
        };
    }
}
 
Layers - group tagged lrs
Код:
/**Скрипт для создания групп слоев в зависимости от названия слоя
 * (в примере в качестве критерия выделения используется нижнее подчеркивание)
 * https://community.adobe.com/t5/photoshop-ecosystem-discussions/layer-selections-based-on-parts-of-the-layername/m-p/11116345
 * https://youtu.be/UuYGSsv9RHk
 */
#target photoshop
s2t = stringIDToTypeID;
(ref = new ActionReference()).putProperty(s2t('property'), p = s2t('numberOfLayers'));
ref.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
var len = executeActionGet(ref).getInteger(p);
var lrs = {}
for (var i = 1; i <= len; i++) {
    (ref = new ActionReference()).putProperty(s2t('property'), p = s2t('name'));
    ref.putIndex(s2t('layer'), i);
    n = executeActionGet(ref).getString(p).split('_');
    if (n.length > 1) {
        (ref = new ActionReference()).putProperty(s2t('property'), p = s2t('layerID'));
        ref.putIndex(s2t('layer'), i);
        var id = executeActionGet(ref).getInteger(p),
            tag = n[n.length - 2]
        if (lrs[tag]) lrs[tag].push(id) else lrs[tag] = [id]
    }
}
for (a in lrs) {
    selectLayerByIDList(lrs[a])
    groupSelectedLrs(a)
}
function selectLayerByIDList(IDList) {
    var ref = new ActionReference()
    for (var i = 0; i < IDList.length; i++) {
        ref.putIdentifier(s2t("layer"), IDList[i])
    }
    var desc = new ActionDescriptor()
    desc.putReference(s2t("null"), ref)
    executeAction(s2t("select"), desc, DialogModes.NO)
}
function groupSelectedLrs(tag) {
    (ref = new ActionReference()).putClass(s2t("layerSection"));
    (desc = new ActionDescriptor()).putReference(s2t("null"), ref);
    (ref1 = new ActionReference()).putEnumerated(s2t("layer"), s2t("ordinal"), s2t("targetEnum"));
    desc.putReference(s2t("from"), ref1);
    (desc1 = new ActionDescriptor()).putString(s2t("name"), tag);
    desc.putObject(s2t("using"), s2t("layerSection"), desc1);
    executeAction(s2t("make"), desc, DialogModes.NO);
}
 
Layers - make radial selection
Код:
/**Создание радиального выделения с заданными отступами от границ документа
https://community.adobe.com/t5/photoshop-ecosystem-discussions/scripting-radial-cutout-s/td-p/12637485
https://youtu.be/Wu2iCI2punY
*/

#target photoshop

var selWidth = 50, //percents
    selHeight = 100, //percents
    doc = new AM('document'),
    res = doc.getProperty('resolution'),
    docWidth = doc.getProperty('width') * res / 72,
    docHeight = doc.getProperty('height') * res / 72;

doc.makeEllipseSelection(
    (docHeight - docHeight * selHeight / 100) / 2,
    (docWidth - docWidth * selWidth / 100) / 2,
    docHeight - (docHeight - docHeight * selHeight / 100) / 2,
    docWidth - (docWidth - docWidth * selWidth / 100) / 2,
    true
)

function AM(target) {
    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'), 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'), s2t('targetEnum'));
        try { return executeActionGet(r).hasKey(property) } catch (e) { return false }
    }

    this.descToObject = function (d) {
        var o = {}
        for (var i = 0; i < d.count; i++) {
            var k = d.getKey(i)
            o[t2s(k)] = getDescValue(d, k)
        }
        return o
    }

    this.makeEllipseSelection = function (top, left, bottom, right, AntA) {
        (r = new ActionReference()).putProperty(s2t('channel'), s2t('selection'));
        (d = new ActionDescriptor()).putReference(s2t('target'), r);
        (d1 = new ActionDescriptor()).putUnitDouble(s2t('top'), s2t('pixelsUnit'), top);
        d1.putUnitDouble(s2t('left'), s2t('pixelsUnit'), left);
        d1.putUnitDouble(s2t('bottom'), s2t('pixelsUnit'), bottom);
        d1.putUnitDouble(s2t('right'), s2t('pixelsUnit'), right);
        d.putObject(s2t('to'), s2t('ellipse'), d1);
        d.putBoolean(s2t('antiAlias'), AntA);
        executeAction(s2t('set'), d, DialogModes.NO);

    }
    function getDescValue(d, k) {
        switch (d.getType(k)) {
            case DescValueType.OBJECTTYPE: return { type: t2s(d.getObjectType(k)), value: d.getObjectValue(k) };
            case DescValueType.LISTTYPE: return d.getList(k);
            case DescValueType.REFERENCETYPE: return d.getReference(k);
            case DescValueType.BOOLEANTYPE: return d.getBoolean(k);
            case DescValueType.STRINGTYPE: return d.getString(k);
            case DescValueType.INTEGERTYPE: return d.getInteger(k);
            case DescValueType.LARGEINTEGERTYPE: return d.getLargeInteger(k);
            case DescValueType.DOUBLETYPE: return d.getDouble(k);
            case DescValueType.ALIASTYPE: return d.getPath(k);
            case DescValueType.CLASSTYPE: return d.getClass(k);
            case DescValueType.UNITDOUBLE: return (d.getUnitDoubleValue(k));
            case DescValueType.ENUMERATEDTYPE: return { type: t2s(d.getEnumerationType(k)), value: t2s(d.getEnumerationValue(k)) };
            default: break;
        };
    }
}
 
Layers - make separate paths from selection
Код:
/**Деление выделения на сегменты, заливка каждого сегмента произвольным цветом
 * https://community.adobe.com/t5/photoshop-ecosystem/is-it-possible-to-automate-filling-multiple-selections-with-random-colors/m-p/12348736
 * https://youtu.be/9dBFSqCK7ng
 */
#target photoshop
activeDocument.suspendHistory('fill selection', 'main()')
function main() {
    var doc = new AM('document'),
        lr = new AM('layer'),
        pth = new AM('path'),
        channel = new AM('channel'),
        presentColors = {};
    doForcedProgress("", "fillSelection()")
    function fillSelection() {
        if (doc.hasProperty('selection')) {
            lr.makeChannelFromSelection('selection')
            lr.makePathFromSelection(1);
            lr.select(lr.newLayer('colors'))
            var pathContents = pth.getProperty('pathContents').value.getList(stringIDToTypeID('pathComponents'))
            try {
                channel.delete('current fill')
                channel.makeSelection('selection')
            } catch (e) { }
            for (var i = 0; i < pathContents.count; i++) {
                updateProgress(i + 1, pathContents.count);
                changeProgressText(i);
                pth.makePathFromSubpath(pathContents.getObjectValue(i))
                pth.makeSelectionFromPath()
                lr.expandSelection(1)
                lr.makeChannelFromSelection('current fill')
                channel.select('current fill')
                doc.deselect()
                channel.invert()
                channel.makeSelection('selection')
                channel.subtractFromSelection('current fill')
                channel.delete('current fill')
                lr.fillByRandomColor(presentColors)
            }
            lr.deselect()
            channel.delete('selection')
            pth.delete()
        }
    }
}
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)
    }
    this.select = function (id) {
        var r = new ActionReference();
        if (typeof id == 'number') r.putIdentifier(target, id) else r.putName(target, id);
        (d = new ActionDescriptor()).putReference(s2t('null'), r);
        executeAction(s2t('select'), d, DialogModes.NO);
    }
    this.makeSelectionFromPath = function () {
        (r = new ActionReference()).putProperty(s2t('channel'), s2t('selection'));
        (d = new ActionDescriptor()).putReference(s2t('null'), r);
        (r1 = new ActionReference()).putProperty(s2t('path'), s2t('workPath'));
        d.putReference(s2t('to'), r1);
        d.putBoolean(s2t('vectorMaskParams'), true);
        executeAction(s2t('set'), d, DialogModes.NO);
    }
    this.deleteCurrentPath = function () {
        (r = new ActionReference()).putProperty(s2t('path'), s2t('workPath'));
        (d = new ActionDescriptor()).putReference(s2t('null'), r);
        executeAction(s2t('delete'), d, DialogModes.NO);
    }
    this.makePathFromSelection = function (tolerance) {
        tolerance = tolerance ? tolerance : 10;
        (r = new ActionReference()).putClass(s2t('path'));
        (d = new ActionDescriptor()).putReference(s2t('null'), r);
        (r1 = new ActionReference()).putProperty(s2t('selectionClass'), s2t('selection'));
        d.putReference(s2t('from'), r1);
        d.putUnitDouble(s2t('tolerance'), s2t('pixelsUnit'), tolerance);
        executeAction(s2t('make'), d, DialogModes.NO);
    }
    this.makePathFromSubpath = function (pth) {
        (r = new ActionReference()).putProperty(stringIDToTypeID("path"), stringIDToTypeID("workPath"));
        (d = new ActionDescriptor()).putReference(stringIDToTypeID("null"), r);
        (l = new ActionList()).putObject(stringIDToTypeID("pathComponent"), pth);
        d.putList(stringIDToTypeID("to"), l);
        executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
    }
    this.newLayer = function (name) {
        (d1 = new ActionDescriptor()).putString(s2t('name'), name);
        (d = new ActionDescriptor()).putObject(s2t('new'), s2t('layer'), d1);
        return (executeAction(s2t('make'), d, DialogModes.NO)).getInteger(s2t('layerID'))
    }
    this.makeChannelFromSelection = function (name) {
        (d = new ActionDescriptor()).putString(s2t("name"), name);
        d.putEnumerated(s2t("colorIndicates"), s2t("maskIndicator"), s2t("selectedAreas"));
        (d1 = new ActionDescriptor()).putObject(s2t("new"), s2t("channel"), d);
        (r = new ActionReference()).putProperty(s2t("channel"), s2t("selection"));
        d1.putReference(s2t("using"), r);
        executeAction(s2t("make"), d1, DialogModes.NO);
    }
    this.deselect = function () {
        (r = new ActionReference()).putProperty(s2t('channel'), s2t('selection'));
        (d = new ActionDescriptor()).putReference(s2t('null'), r);
        d.putEnumerated(s2t('to'), s2t('ordinal'), s2t('none'));
        executeAction(s2t('set'), d, DialogModes.NO);
    }
    this.invert = function () {
        executeAction(s2t("invert"), undefined, DialogModes.NO);
    }
    this.makeSelection = function (name) {
        (r = new ActionReference()).putProperty(target, s2t('selection'));
        (d = new ActionDescriptor()).putReference(s2t('null'), r);
        (r1 = new ActionReference()).putName(target, name);
        d.putReference(s2t('to'), r1);
        executeAction(s2t('set'), d, DialogModes.NO);
    }
    this.subtractFromSelection = function (name) {
        (r = new ActionReference()).putName(target, name);
        (d = new ActionDescriptor()).putReference(s2t('null'), r);
        (r1 = new ActionReference()).putProperty(target, s2t('selection'));
        d.putReference(s2t('from'), r1);
        executeAction(s2t('subtract'), d, DialogModes.NO);
    }
    this.expandSelection = function (px) {
        (d = new ActionDescriptor()).putUnitDouble(s2t('by'), s2t('pixelsUnit'), px);
        executeAction(s2t('expand'), d, DialogModes.NO);
    }
    this.delete = function (name) {
        var r = new ActionReference();
        if (name) r.putName(target, name) else r.putEnumerated(target, s2t('ordinal'), s2t('targetEnum'));
        (d = new ActionDescriptor()).putReference(s2t('null'), r);
        executeAction(s2t('delete'), d, DialogModes.NO);
    }
    this.fillByRandomColor = function (colorObj) {
        var c = new SolidColor;
        with (c.rgb) {
            do {
                red = Math.random() * 255
                green = Math.random() * 255
                blue = Math.random() * 255
            } while (colorObj[hexValue])
            colorObj[hexValue] = true;
            (d = new ActionDescriptor()).putEnumerated(s2t("using"), s2t("fillContents"), s2t("color"));
            (d1 = new ActionDescriptor()).putDouble(s2t("red"), red);
            d1.putDouble(s2t("green"), green);
            d1.putDouble(s2t("blue"), blue);
            d.putObject(s2t("color"), s2t("RGBColor"), d1);
            executeAction(s2t("fill"), d, DialogModes.NO);
        }
    }
    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;
        };
    }
}
 
Вот такой вопрос к вам, как к специалисту. Есть ли в CS2 какой то способ добраться из скрипта до текущего выбранного инструмента и до свойств кисти? В более поздних понятно как, но в CS2?
 
Я не знаю способа как нормально добраться до текущего инструмента в CS2.

Но можно через одно место: :D
а) сохранить текущие пресеты инструментов в файл
б) удалить все пресеты
в) создать новый пресет. Он будет содержать в себе только имя текущего инструмента и его настройки
г) сохранить файл пресета
д) прочитать бинарник - в нем будет только активный инструмент и его настройки (при необходимости можно поправить и загрузить обратно)
е) загрузить обратно пресеты пользователя из файла

Тут только одна проблема - разобраться в бинарнике (старый формат файла пресетов отличается от нового, документацию я сходу не нашел. На вид там всё не очень сложно, но я читать его не пробовал).
 
Последнее редактирование:
Я тоже об этом думал.
Но, на мой взгляд, это сильно деструктивно только для того, чтобы поменять настройки кисти, не меняя текущий выбранный инструмент :(
 
Ну в норме это максимум секунду займет, т.е. попытаться можно. Я попробовал быстренько накидать решение "в лоб" (без анализа файла, просто поиском дескриптора внутри пресета) и словил кучу проблем - в CS2 не работает блок try...catch, а без него пресеты не удалить. Можно, конечно, генерировать пустой файл-заглушку и подгружать его. Плюс некоторые параметры дескриптора не могут быть получены по typeID (не понял почему), но можно не заморачиваться и получать только нужные (дескриптор brush вроде доступен))



В новых фотошопах все, естественно, работает. Код только на чтение параметров.
Код:
#target photoshop
var source = new File(Folder.temp.fsName + "/" + "CPFL"),
    tmp = new File(Folder.temp.fsName + "/" + "TPFL");
saveCurrentPresets(source)
deletePresets()
newPreset('ToolSettings')
saveCurrentPresets(tmp)
loadPrests(source)

tmp.open("r");
tmp.encoding = "BINARY";
var s = tmp.read()
tmp.remove()

for (var i = 0; i < s.length; i++) {
    var x = s.substr(i);
    var d = new ActionDescriptor();
    try { d.fromStream(x); break } catch (e) { }
    if (i > 500) break;
}

var n = new ActionDescriptor();
n.putObject(s2t('null'), s2t('null'), d);

var s = t2s(n.getObjectType(n.getKey(0))) + '\n';
alert(s += checkDesc(d))

function saveCurrentPresets(pth) {
    (d = new ActionDescriptor()).putPath(s2t("null"), pth);
    (r = new ActionReference()).putProperty(s2t("property"), s2t("toolPreset"));
    r.putEnumerated(s2t("application"), s2t("ordinal"), s2t("targetEnum"));
    d.putReference(s2t("to"), r);
    executeAction(s2t("set"), d, DialogModes.NO);
}

function deletePresets() {
    var index = 1;
    do {
        try {
            (r = new ActionReference()).putIndex(s2t("toolPreset"), index++);
            (l = new ActionList()).putReference(r);
            (d = new ActionDescriptor()).putList(s2t("null"), l);
            executeAction(s2t("delete"), d, DialogModes.NO);
        }
        catch (e) { break; }
    } while (true)

}

function loadPrests(pth) {
    (r = new ActionReference()).putProperty(s2t("property"), s2t("toolPreset"));
    r.putEnumerated(s2t("application"), s2t("ordinal"), s2t("targetEnum"));
    (d = new ActionDescriptor()).putReference(s2t("null"), r);
    d.putPath(s2t("to"), pth);
    executeAction(s2t("set"), d, DialogModes.NO);
}

function newPreset(nm) {
    (r = new ActionReference()).putClass(s2t("toolPreset"));
    (d = new ActionDescriptor()).putReference(s2t("null"), r);
    (r1 = new ActionReference()).putProperty(s2t("property"), s2t("currentToolOptions"));
    r1.putEnumerated(s2t("application"), s2t("ordinal"), s2t("targetEnum"));
    d.putReference(s2t("using"), r1);
    d.putString(s2t("name"), nm);
    executeAction(s2t("make"), d, DialogModes.NO);
}
function checkDesc(d) {
    var c = d.count,
        str = '';
    for (var i = 0; i < c; i++) {
        str += t2s(d.getKey(i)) +
            ' = ' + getValues(d, i) + '\n';
    };
    return str
};

function getValues(d, keyNum) {
    var p = d.getKey(keyNum);
    switch (d.getType(p)) {
        case DescValueType.OBJECTTYPE: return (d.getObjectValue(p) + '_' + t2s(d.getObjectType(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.getSstring(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) + '_' + t2s(d.getUnitDoubleType(p)));
        case DescValueType.ENUMERATEDTYPE: return (t2s(d.getEnumerationValue(p)) + '_' + t2s(d.getEnumerationType(p)));
        case DescValueType.RAWTYPE:
            var tempStr = d.getData(p);
            var rawData = new Array();
            for (var tempi = 0; tempi < tempStr.length; tempi++) {
                rawData[tempi] = tempStr.charCodeAt(tempi);
            }
            return rawData;
        default:
            break;
    };
};

function s2t(s) { return stringIDToTypeID(s) }
function t2s(t) { if (!typeIDToStringID(t)) { return typeIDToCharID(t) } else { return typeIDToStringID(t) } }
 
Последнее редактирование:
У меня в CS2 нет LARGEINTEGERTYPE
 
  • Спасибо
Реакции: jazzy
а... то есть просто такой константы нет и при вызове он брейкается. Понял.
 
Последнее редактирование:
На самам деле, проблема даже не в этом. Отсутствие try...catch закостылить еще не так с большой руки (скажем, количество пресетов берем из ActionList presetManager, парсинг бинарника без посимвольного перебора тоже как то можно запилить)
Главная проблема в том, что в дескрипторе кисти я не нахожу нужных свойств opacity и usePressureOverridesOpacity :(
1645960460925.png
 
opacity точно есть, а вот второй параметр возможно как-то по другому называется (нужно тыркать, смотреть в какой переменной значение меняется).
Вообще, если мы сами создаем пресет, то мы знаем его имя. В файлах пресетов имена по сути сохраняются в неизменном виде (char + 0 символ), т.е. создавая уникальное имя мы можем быстро найти где оно лежит в файле пресета и уже плясать оттуда (дескриптор как правило лежит через нуль символ после имени, т.е. смещение фиксированное).

Код:
var a = 'TEST_PRESET_NAME',
    c = [];
for (var i = 0; i < a.length; i++) c.push(a.substr(i, 1))
var z = c.join(String.fromCharCode(0))
var from = s.indexOf(z)

В новом шопе с файлом старых пресетов из CS2 это работает. А вот в самом CS2 не завелось. Мне сложно в нем работать - виртуалка мешает, а на живую систему ставить не хочу.

1645960460925.jpg
2022-02-27_15-26-00.png
 
Последнее редактирование:
  • Спасибо
Реакции: _MBK_
Гм, интересная идея, получается, подобным способом можно найти созданный пресет прямо в файле без удаления всех пресетов.
Ну хорошо, допустим, мы нашли соответствие между свойствами, а устанавливать то их как? У меня такая конструкция в CS2 не заводится даже с закомментированными строками :(
JavaScript:
function SetPaintBrush(mode,opacity,flow,penopacity,pensize,penair) {
  var idset = stringIDToTypeID( "set" );
  var desc226 = new ActionDescriptor();
  var idnull = stringIDToTypeID( "null" );
  var ref170 = new ActionReference();
    var idPbTl = stringIDToTypeID( "paintbrushTool" );
    ref170.putClass( idPbTl );
    desc226.putReference( idnull, ref170 );
    var id12 = stringIDToTypeID( "to" );
    var desc5 = new ActionDescriptor();
    // opacity
  //  var id13 = stringIDToTypeID( "opacity" );
  //  var id14 = stringIDToTypeID( "percentUnit" );
  // desc5.putUnitDouble( id13, id14, opacity );
    // blend mode
  //  var id15 = stringIDToTypeID( "mode" );
  //  var id16 = stringIDToTypeID( "blendModel" );
  //var id17 = stringIDToTypeID( mode );
   // desc5.putEnumerated( id15, id16, id17 );
    // flow
  //  var id19 = stringIDToTypeID( "flow" );
 // desc5.putUnitDouble( id19, id14, flow );
    // pressure for opacity
   // desc5.putBoolean( stringIDToTypeID( "usePressureOverridesOpacity" ), penopacity );
    // pressure for size
   //  desc5.putBoolean( stringIDToTypeID( "usePressureOverridesSize" ), pensize );
    // enable air brush
  //  desc5.putBoolean( stringIDToTypeID( "repeat" ), penair );
    var id18 = stringIDToTypeID( "null" );
    desc226.putObject( id12, id18, desc5 );
  executeAction( idset, desc226, DialogModes.NO );   
  }
 
Переводить дескриптор в строку функцией .toSource(), писать ее обратно в файл пресетов, загружать файл, активировать пресет. Не уверен, правда, что после обновления дескриптора и обратной конвертации в строку он будет того же размера в байтах (т.е. файл может побиться).
 
  • Спасибо
Реакции: _MBK_
Ну тогда проще в самом бинарном файле править значения без конвертации.
То есть, все таки без файлового буфера пресета чисто дескриптором никак? :(
 
Последнее редактирование: