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

Нет.
 
Код:
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 не завелось.
Не работает потому, что в старых фотошопах работа идет не с бинарными, а текстовыми (т.e. null-terminated строками)
Выкрутиться можно, реализовав indexOf подобным неуклюжим образом:
JavaScript:
function getPresetData(s,name)
{
var i;
var k=0;
var fl=0;
for (i = 0; i < s.length-name.length; i++)
{
 
  if (name.substr(k, 1)==s.substr(i, 1))
  {
    if (fl==0)
    {
     fl=1;
   
    }
    k++;
    if (k==name.length) break;
  } else
  if (fl==1) {
   fl=0;k=0;
  }
}
return (k==name.length)?i:-1;
}
 
Последнее редактирование:
  • Спасибо
Реакции: jazzy
Да, я уже разобрался, спасибо. Тоже переписал на посимвольное сравнение. А то мне это покоя не давало :)
 
Последнее редактирование:
В принципе все получилось и с патчем пресета, единственная незначительная странность осталась
По какой то метафизической причине описанный выше алгоритм не работает для поиска юникодных строк начинающихся с маленькой буквы. То есть, пресет под названием CurrentBrush находит верно а вот сurrentBrush хоть и находит, но возвращает совершенно левое число. Сколько не бился так и не понял почему, но это несущественно 'hz'
 
Events - auto select layer transparency
Код:
/**Автоматическое создание выделения при активации слоя
 * https://community.adobe.com/t5/photoshop-ecosystem-discussions/choose-a-layer-and-make-it-a-selection/td-p/12795014
 * https://youtu.be/gBOgKLuQX8s
 */

#target photoshop

var s2t = stringIDToTypeID,
    t2s = typeIDToStringID;
try {
    var target = t2s(arguments[0].getReference(s2t('null')).getDesiredClass());
    if (target == 'layer' && (ScriptUI.environment.keyboardState.ctrlKey || ScriptUI.environment.keyboardState.metaKey)) {
        (d = new ActionDescriptor()).putReference(s2t('target'), (function () { (r = new ActionReference()).putProperty(t = s2t('channel'), s2t('selection')); return r }()));
        d.putReference(s2t('to'),  function(){(r = new ActionReference()).putEnumerated(t, t, s2t('transparencyEnum')); return r}());
        executeAction(s2t('set'), d, DialogModes.NO);
    }
} catch (e) { alert(e) }
if (!target) {
    var f = File($.fileName),
        del;
    for (var i = 0; i < app.notifiers.length; i++) {
        var ntf = app.notifiers[i]
        if (ntf.eventFile.name == f.name) { ntf.remove(); i--; del = true }
    }
    if (del) {
        alert('event listening disabled!')
    } else {
        app.notifiers.add('slct', f, 'Lyr ')
        alert('event listening enabled!')
    }
}
 
Events - copy linked

В фотошопе есть досадный баг, который тянется еще с 3 версии - если вы копируете связанные слои, то после копирования сбрасываются настройки их видимости. Скрипт отслеживает видимость слоев в момент выделения и после копирования проверяет параметры видимости.
Код:
/**Контроль видимости связанных слоев при создании дубликатов
 * https://community.adobe.com/t5/photoshop-ecosystem-bugs/p-hidden-layers-linked-layer-made-visible-when-duplicated/idc-p/12802559
 * https://www.youtube.com/watch?v=q_9uocqy2Gw
 */

#target photoshop;
var UUID = '7c9fb9b1-890c-49ee-91cc-aab4cc85efc2',
    s2t = stringIDToTypeID,
    t2s = typeIDToStringID,
    layerID = s2t('layerID'),
    visible = s2t('visible'),
    layer = s2t('layer');
try {
    var evt = t2s(arguments[1]),
        tgt = t2s(arguments[0].getReference(s2t('null')).getDesiredClass());
    if (evt && (tgt.indexOf('ayer') > 0 || tgt == 'layerSection')) {
        var lrs = getLayers();
        if (evt == 'select' || evt == 'make') {
            putCustomOptions(UUID, (function (l) { (d = new ActionDescriptor()).putList(layer, l); return d })(findSelectedLayers(lrs.selection.reverse(), lrs.layers)), false)
        } else if (evt == 'duplicate') {
            var a = findSelectedLayers(lrs.selection.reverse(), lrs.layers);
            try { var d = getCustomOptions(UUID) } catch (e) { }
            if (d && a.count == (b = d.getList(layer)).count) {
                var ids = []
                for (var i = 0; i < a.count; i++) {
                    if (a.getObjectValue(i).getBoolean(visible) != b.getObjectValue(i).getInteger(visible))
                        ids.push(a.getObjectValue(i).getInteger(layerID))
                }
                if (ids.length) hideLayers(ids)
            }
        }
    }
} catch (e) { }
if (!evt) {
    var f = File($.fileName),
        del;
    for (var i = 0; i < app.notifiers.length; i++) {
        var ntf = app.notifiers[i]
        if (ntf.eventFile.name == f.name) { ntf.remove(); i--; del = true }
    }
    if (del) {
        alert('event listening disabled!')
    } else {
        app.notifiers.add('slct', f, 'Lyr ')
        app.notifiers.add('Dplc', f, 'Lyr ')
        app.notifiers.add('Mk  ', f)
        alert('event listening enabled!')
    }
}
function getLayers() {
    (r = new ActionReference()).putProperty(s2t("property"), s2t("json"));
    r.putEnumerated(s2t("document"), s2t("ordinal"), s2t("targetEnum"));
    (d = new ActionDescriptor()).putReference(s2t("null"), r);
    eval("var a=" + executeAction(s2t("get"), d, DialogModes.NO).getString(s2t("json")));
    return a;
}
function findSelectedLayers(idx, lrs, result, collect) {
    if (!result) result = new ActionList();
    for (var a in lrs) {
        if (!idx.length && !collect) return result
        if (equal = lrs[a].index == idx[0] || collect) {
            if (equal) idx.shift();
            var d = new ActionDescriptor();
            d.putInteger(layerID, lrs[a].id);
            d.putBoolean(visible, lrs[a].visible)
            result.putObject(layer, d)
            if (lrs[a].type == 'layerSection') findSelectedLayers(idx, lrs[a].layers, result, true)
        }
        if (lrs[a].type == 'layerSection') findSelectedLayers(idx, lrs[a].layers, result)
    }
    return result;
}
function hideLayers(ids) {
    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('hide'), d, DialogModes.NO);
}
 
Events - rename layer by current brush preset
Код:
/**Переименование слоя по имени кисти, которой на нем рисуют
 * https://community.adobe.com/t5/photoshop-ecosystem-discussions/javascript-get-current-brush-shape-name/td-p/12783812
 * https://youtu.be/kc9qTR5ms2c
 *
 * Script renames current layer to brush preset's name
 * Usage:
 * - save script in file
 * - first run: event listener enabled
 * - next run: event listener disabled
 */

#target photoshop
var s2t = stringIDToTypeID,
    t2s = typeIDToStringID;
try {
    var target = t2s(arguments[0].getReference(s2t('null')).getDesiredClass());
    if (target == 'brush') {
        (r = new ActionReference()).putEnumerated(s2t('layer'), s2t('ordinal'), s2t('targetEnum'));
        (d = new ActionDescriptor()).putReference(s2t('target'), r);
        (d1 = new ActionDescriptor()).putString(s2t('name'), arguments[0].getReference(s2t('null')).getName());
        d.putObject(s2t('to'), s2t('layer'), d1);
        executeAction(s2t('set'), d, DialogModes.NO);
    }
} catch (e) {}
if (!target) {
    app.notifiersEnabled = true
    var f = File($.fileName),
        deleted;
    for (var i = 0; i < app.notifiers.length; i++) {
        var ntf = app.notifiers[i]
        if (ntf.eventFile.name == f.name) { ntf.remove(); i--; deleted = true }
    }
    if (deleted) {
        alert('event listening disabled!')
    } else {
        app.notifiers.add('slct', f)
        alert('event listening enabled!')
    }
}
 
Layers - transform ratio to CSV
Код:
/**Получить масштаб трансформации всех смарт-объектов документа и сохранить их в виде таблицы
 * https://community.adobe.com/t5/photoshop-ecosystem-discussions/script-to-export-all-smart-objects-layer-names-with-scale/m-p/12799010#M628167
 * https://youtu.be/MPk_P-Yfs44
 */
#target photoshop;
var 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),
    div = ';'
csv = ['name' + div + 'scale']
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('name'));
        r.putIndex(s2t('layer'), i);
        var n = executeActionGet(r).getString(p) + div;
        (r = new ActionReference()).putProperty(s2t('property'), p = s2t('smartObjectMore'));
        r.putIndex(s2t('layer'), i);
        var t = executeActionGet(r).getObjectValue(p).getList(s2t('transform'));
        csv.push(n + (Math.round((Math.sqrt(Math.pow(t.getDouble(0) - t.getDouble(2), 2) + Math.pow(t.getDouble(1) - t.getDouble(3), 2))) /
            executeActionGet(r).getObjectValue(p).getObjectValue(s2t('size')).getDouble(s2t('width')) * 10000) / 100)+'%')
    }
}
n = (new File(Folder.desktop + '/scale')).saveDlg('Save file', '*.csv');
if (n) {
    if (n.open('w', 'TEXT')) {
        n.write(csv.join('\n'))
        n.close()
    }
}
* корректно работает только с пропорциональным масштабированием. Не работает с афинными преобразованиями.
 
Одновременный запуск 2-х и более экземпляров фотошоп на одном компьютере.

Скрипт под винду. Код попросили не светить (чтобы лавочку не прикрыли), поэтому обфусцирован
Код:
@JSXBIN@ES@2.0@MyBbyBn0AHJBnABjzBjGBfEjzEiGjJjMjFCfRBCzBhLDXzEjUjFjNjQEfjzGiGjPj
MjEjFjSFfnneGhPjQhOjCjBjUffnfJCnAEXzEjPjQjFjOGfjBfRBFeBjXffJDnAEXzHjXjSjJjUjFjM
jOHfjBfRBCDCDnXzGjGjTiOjBjNjFIfEjCfRBCDjzEjQjBjUjIJfnneOhPiQjIjPjUjPjTjIjPjQhOj
FjYjFffeKjTjUjBjSjUhAhChChAhCnnneJhChAhNiTjFjSjWjFjSffJEnAEXzFjDjMjPjTjFKfjBfnf
JFnAEXzHjFjYjFjDjVjUjFLfjBfnfJGnAEXzFjTjMjFjFjQMfjzBhENfRBFd2mcFffJHnAEXzGjSjFj
NjPjWjFOfjBfnf0DzAPByB
 
И сразу, чтобы далеко не ходить:


Код:
@JSXBIN@ES@2.0@MyBbyBn0ABgAbyBn0AOJBnASzBjXByBEjzGiXjJjOjEjPjXCfREFeGjEjJjBjMjPj
HjzJjVjOjEjFjGjJjOjFjEDfjDfWzGiPjCjKjFjDjUEBzKjSjFjTjJjajFjBjCjMjFFFctftnftJCnA
BXzEjUjFjYjUGfVBfyBneUiSjVjOhAjTjFjDjPjOjEhAjJjOjTjUjBjOjDjFhAfJDnASzCjEjMHyBEX
zDjBjEjEIfVBfyBREFeMjEjSjPjQjEjPjXjOjMjJjTjUjDfjDfWEBzEjOjBjNjFJFeCjEjMffnftJEn
ABXzFjXjJjEjUjIKfXzNjQjSjFjGjFjSjSjFjEiTjJjajFLfVHfyBndmIfJGnASzBjHMyBEXIfVBfyB
RDFeFjHjSjPjVjQjDfWEBJFeBjHffnftJHnABXzLjPjSjJjFjOjUjBjUjJjPjONfVMfyBneDjSjPjXf
JInABXzNjBjMjJjHjOiDjIjJjMjEjSjFjOOfVMfyBARCFeEjMjFjGjUFeGjDjFjOjUjFjSfnfJKnASz
CjPjLPyBEXIfVMfyBREFeGjCjVjUjUjPjOjDfFeDiSjVjOWEBJFeCjPjLffnftJNnAEXIfVMfyBREFe
GjCjVjUjUjPjOjDfFeGiDjBjOjDjFjMWEBJFeGjDjBjOjDjFjMffJQnASzBjCQyBWEAnftaRbTn0ACJ
TnASzBjBRyBEXzOjHjFjUiEjJjTjQjMjBjZiOjBjNjFSfjzKiCjSjJjEjHjFiUjBjMjLTfRBCzBhLUn
CzBhKVVzBjJWfyBnndKeKjQjIjPjUjPjTjIjPjQhNnffnftOUbyWn0ABJWnABQzAXfVQfyBVRfyBCUC
UnEXzKjHjFjUiBjQjQiQjBjUjIYfjTfRBCUnCVVWfyBnndKeKjQjIjPjUjPjTjIjPjQhNnffeKjTjUj
BjSjUhAhChChAhCnnneJhChAhNiTjFjSjWjFjSnfAVRfyBnAVWfyBGFdZByBzBhcZJganABXzHjPjOi
DjMjJjDjLgafVPfyBNyBnAMgabyBn0AIJgbnABjzCjGjMgbfEjzEiGjJjMjFgcfRBCUjzRjQjSjFjGj
FjSjFjOjDjFjTiGjPjMjEjFjSgdfnneGhPjahOjCjBjUffnfJgcnAEXzEjPjQjFjOgefjgbfRBFeBjX
ffJgdnABXzIjFjOjDjPjEjJjOjHgffjgbfneEjUjFjYjUfJgenAEXzHjXjSjJjUjFjMjOhAfjgbfRBQ
XfjQfXGfXzJjTjFjMjFjDjUjJjPjOhBfjHfffJgfnAEXzFjDjMjPjTjFhCfjBfnfJhAnAEXhCfjgbfn
fJhBnAEXzHjFjYjFjDjVjUjFhDfjgbfnfJhCnAEXzFjTjMjFjFjQhEfjzBhEhFfRBFd2mQHff0DXChD
nfJhEnABXzGjPjOiTjIjPjXhGfVBfyBNyBnAMhEbyBn0ACLyhFJhFnAEXIfjHfRCFeEjJjUjFjNVRfA
ffAVRfAjQfyBXfOhGJhGnABXhBfjHfEXzEjGjJjOjEhHfjHfRBEXSfjTfRBFeJjQjIjPjUjPjTjIjPj
QffffnfAXzGjMjFjOjHjUjIhIfXzFjJjUjFjNjThJfjHfJhHnABXzHjFjOjBjCjMjFjEhKfjHfBXhKf
jPfncffnfABR40BiAABAXChInfJhKnAEXzEjTjIjPjXhLfVBfyBnfABnzBjFhMnnAHW4F0AiAP4D0Ai
AB40BiAH4B0AiAM4C0AiAR4G0AiAQ4E0AiAAHAXByB
 
Одновременный запуск 2-х и более экземпляров фотошоп на одном компьютере.

Скрипт под винду. Код попросили не светить (чтобы лавочку не прикрыли), поэтому обфусцирован
Код:
@JSXBIN@ES@2.0@MyBbyBn0AHJBnABjzBjGBfEjzEiGjJjMjFCfRBCzBhLDXzEjUjFjNjQEfjzGiGjPj
MjEjFjSFfnneGhPjQhOjCjBjUffnfJCnAEXzEjPjQjFjOGfjBfRBFeBjXffJDnAEXzHjXjSjJjUjFjM
jOHfjBfRBCDCDnXzGjGjTiOjBjNjFIfEjCfRBCDjzEjQjBjUjIJfnneOhPiQjIjPjUjPjTjIjPjQhOj
FjYjFffeKjTjUjBjSjUhAhChChAhCnnneJhChAhNiTjFjSjWjFjSffJEnAEXzFjDjMjPjTjFKfjBfnf
JFnAEXzHjFjYjFjDjVjUjFLfjBfnfJGnAEXzFjTjMjFjFjQMfjzBhENfRBFd2mcFffJHnAEXzGjSjFj
NjPjWjFOfjBfnf0DzAPByB
А что тут сверхсекрктного? Этот способ здесь давно описывали, ЕМНИС, Бутрин, кажись 'hmmm'
 
  • Спасибо
Реакции: jazzy
Честно - искал упоминания о таком способе и не нашел. Спросил на внутреннем форуме у сотрудников - сказали, молодец что нашел, но ты подписывал NDA ¯\_(ツ)_/¯
 
Последнее редактирование:
Ну я сходу нагуглить тоже не могу, но все ж понимают, какой сейчас гугл?
Но на этом форуме такая тема точно, была, причем, данный ключ, насколько я помню, не только для фотошопа работает.
 
  • Спасибо
Реакции: jazzy
Понятно. Я просто искал способ асинхронного выполнения bridgeTalk при наличии только одной программы в системе. Помнил, что кто-то запускал второй экземпляр через ключ, но сам ключ не нашел. Поэтому решил собрать строковые переменные из бинарников и скормить их в качестве ключей. Т.е. чисто брутфорсом.