Skip to main content
Participating Frequently
August 15, 2021
Answered

How to Create a Small Script?

  • August 15, 2021
  • 2 replies
  • 1025 views

Hey everyone,

 

For those who make scripts for Photoshop, I was wondering if you could make me a really small script to change a pattern. 

 

I'm editing a large amount of PSDs in batch to quickly change a Layer Style Pattern, but which will also maintain the scale that the current pattern is at. If you change a pattern using an action, it does not save the scale of the current pattern even though it is just a different colouration of the same pattern. If I did this same thing with inside Photoshop, not using an action, there is no problem with changing a pattern, and it doesn't affect the scale at all. 

The steps should be simple:
- get layer named 'Pattern' 

- get layer pattern scale and save as a variable ? (I'm not sure if this is an available variable to get nor if it's needed)

- change layer pattern

- set scale to saved variable

 

Opening and saving is dealt with by bridge, it's just the small action I need to replace with a script that doesn't affect the scale of patterns, and just changes it. 

This topic has been closed for replies.
Correct answer jazz-y

Below you see 2 scripts. Copy the code, save each one separately with the jsx extension (name the files as you like), place the files in the Photoshop presets folder. Restart Photoshop. These files will appear in the scripts menu.

 

The first script gets the scale of the pattern of the current layer and writes it to the layer's metadata (it will be stored with the layer even after you save and reopen the file or copy layer to another document). The second script reads the scale of the pattern from the metadata of the current layer and assigns it.

I think it's clear how to use it - start recording the action, first record the launch of the first script (it will receive and save the scale value for the selected layer), then assign the pattern you need, then record the call to the second script (it will adjust the scale by reading the value written on the previous step). You can also use manual launch of scripts if you need to save and later return to the saved scale.

 

Script 1: Save pattern scale to layer metadata.jsx

 

#target photoshop

var lr = new AM('layer'),
    meta = new LayerMetadata('pattern', 'scale');

if (lr.hasProperty('adjustment')) {
    var adj = lr.convertToObject(lr.getProperty(p = 'adjustment'), p)
    for (o in adj) {
        if (adj[o]._obj == 'patternLayer') {
            var scale = adj[o].scale ? adj[o].scale._value : 100;
            meta.set(activeDocument.activeLayer, 'fill', scale)
            break;
        }
    }
}

if (lr.hasProperty('layerEffects')) {
    var fx = lr.convertToObject(lr.getProperty(p = 'layerEffects').value, 'patternFill')
    if (fx) {
        var scale = fx.scale ? fx.scale._value : 100;
        meta.set(activeDocument.activeLayer, 'fx', scale)
    }
}

function LayerMetadata(customNamespace, prefix) {
    if (ExternalObject.AdobeXMPScript == undefined) ExternalObject.AdobeXMPScript = new ExternalObject('lib:AdobeXMPScript')

    this.set = function (layerObj, key, value) {
        try {
            xmpMeta = new XMPMeta(layerObj.xmpMetadata.rawData)
        } catch (e) { xmpMeta = new XMPMeta() }

        XMPMeta.registerNamespace(customNamespace, prefix)
        xmpMeta.setProperty(customNamespace, key, value)
        layerObj.xmpMetadata.rawData = xmpMeta.serialize()
        return ""
    }

    this.get = function (layerObj, key) {
        try {
            xmpMeta = new XMPMeta(layerObj.xmpMetadata.rawData)
            var data = xmpMeta.getProperty(customNamespace, key)
        } catch (e) { }
        return data == undefined ? "" : data.value
    }
}

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'));
        return executeActionGet(r).hasKey(property)
    }

    this.convertToObject = function (obj, key) {
        var d = new ActionDescriptor();
        switch (obj.typename) {
            case 'ActionList': d.putList(s2t(key), obj); break;
            case 'ActionDescriptor': d = obj; break;
        }
        (desc = new ActionDescriptor()).putObject(s2t('object'), s2t('json'), d);
        eval('var o = ' + executeAction(s2t('convertJSONdescriptor'), desc).getString(s2t('json')));
        return o[key]
    }

    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;
        };
    }
}

 

Script 2: Get pattern scale from layer metadata.jsx

 

#target photoshop

var lr = new AM('layer'),
    meta = new LayerMetadata('pattern', 'scale');

if (lr.hasProperty('adjustment')) {
    var adj = lr.convertToObject(lr.getProperty(p = 'adjustment'), p)
    for (o in adj) {
        if (adj[o]._obj == 'patternLayer') {
            var scale = meta.get(activeDocument.activeLayer, 'fill')
            if (scale) {
                $.writeln(scale)
                lr.setAdjustmentFillScale(lr.getProperty(p), Number(scale))
            }
            break;
        }
    }
}

if (lr.hasProperty('layerEffects')) {
    var fx = lr.convertToObject(lr.getProperty(p = 'layerEffects').value, 'patternFill')
    if (fx) {
        var scale = meta.get(activeDocument.activeLayer, 'fx')
        if (scale) {
            $.writeln(scale)
            lr.setFxFillScale(lr.getProperty(p).value, Number(scale))
        }

    }
}

function LayerMetadata(customNamespace, prefix) {
    if (ExternalObject.AdobeXMPScript == undefined) ExternalObject.AdobeXMPScript = new ExternalObject('lib:AdobeXMPScript')

    this.set = function (layerObj, key, value) {
        try {
            xmpMeta = new XMPMeta(layerObj.xmpMetadata.rawData)
        } catch (e) { xmpMeta = new XMPMeta() }

        XMPMeta.registerNamespace(customNamespace, prefix)
        xmpMeta.setProperty(customNamespace, key, value)
        layerObj.xmpMetadata.rawData = xmpMeta.serialize()
    }

    this.get = function (layerObj, key) {
        try {
            xmpMeta = new XMPMeta(layerObj.xmpMetadata.rawData)
            var data = xmpMeta.getProperty(customNamespace, key)
        } catch (e) { }
        return data == undefined ? "" : data.value

    }
}

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'));
        return executeActionGet(r).hasKey(property)
    }

    this.setAdjustmentFillScale = function (list, scale) {
        for (var i = 0; i < list.count; i++) {
            if (t2s(list.getObjectType(i)) == 'patternLayer') {
                var fill = list.getObjectValue(i)
                fill.putUnitDouble(s2t('scale'), s2t('percentUnit'), scale)
            }
        }
        (r = new ActionReference()).putEnumerated(s2t("contentLayer"), s2t("ordinal"), s2t("targetEnum"));
        (d = new ActionDescriptor()).putReference(s2t("null"), r);
        d.putObject(s2t("to"), s2t("patternLayer"), fill);
        executeAction(s2t("set"), d, DialogModes.NO);

    }

    this.setFxFillScale = function (desc, scale) {
        s2t = stringIDToTypeID;

        var fill = desc.getObjectValue(s2t("patternFill"));
        fill.putUnitDouble(s2t('scale'), s2t('percentUnit'), scale);

        (r = new ActionReference()).putProperty(s2t("property"), p = s2t("layerEffects"));
        r.putEnumerated(s2t("layer"), s2t("ordinal"), s2t("targetEnum"));
        var fx = executeActionGet(r).getObjectValue(p);
        fx.putObject(s2t("patternFill"), s2t("patternFill"), fill);
        (d = new ActionDescriptor()).putReference(s2t("null"), r);
        d.putObject(s2t("to"), s2t("layerEffects"), fx);
        executeAction(s2t("set"), d, DialogModes.NO);
    }

    this.convertToObject = function (obj, key) {
        var d = new ActionDescriptor();
        switch (obj.typename) {
            case 'ActionList': d.putList(s2t(key), obj); break;
            case 'ActionDescriptor': d = obj; break;
        }
        (desc = new ActionDescriptor()).putObject(s2t('object'), s2t('json'), d);
        eval('var o = ' + executeAction(s2t('convertJSONdescriptor'), desc).getString(s2t('json')));
        return o[key]
    }

    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;
        };
    }
}

 

2 replies

jazz-yCorrect answer
Legend
August 15, 2021

Below you see 2 scripts. Copy the code, save each one separately with the jsx extension (name the files as you like), place the files in the Photoshop presets folder. Restart Photoshop. These files will appear in the scripts menu.

 

The first script gets the scale of the pattern of the current layer and writes it to the layer's metadata (it will be stored with the layer even after you save and reopen the file or copy layer to another document). The second script reads the scale of the pattern from the metadata of the current layer and assigns it.

I think it's clear how to use it - start recording the action, first record the launch of the first script (it will receive and save the scale value for the selected layer), then assign the pattern you need, then record the call to the second script (it will adjust the scale by reading the value written on the previous step). You can also use manual launch of scripts if you need to save and later return to the saved scale.

 

Script 1: Save pattern scale to layer metadata.jsx

 

#target photoshop

var lr = new AM('layer'),
    meta = new LayerMetadata('pattern', 'scale');

if (lr.hasProperty('adjustment')) {
    var adj = lr.convertToObject(lr.getProperty(p = 'adjustment'), p)
    for (o in adj) {
        if (adj[o]._obj == 'patternLayer') {
            var scale = adj[o].scale ? adj[o].scale._value : 100;
            meta.set(activeDocument.activeLayer, 'fill', scale)
            break;
        }
    }
}

if (lr.hasProperty('layerEffects')) {
    var fx = lr.convertToObject(lr.getProperty(p = 'layerEffects').value, 'patternFill')
    if (fx) {
        var scale = fx.scale ? fx.scale._value : 100;
        meta.set(activeDocument.activeLayer, 'fx', scale)
    }
}

function LayerMetadata(customNamespace, prefix) {
    if (ExternalObject.AdobeXMPScript == undefined) ExternalObject.AdobeXMPScript = new ExternalObject('lib:AdobeXMPScript')

    this.set = function (layerObj, key, value) {
        try {
            xmpMeta = new XMPMeta(layerObj.xmpMetadata.rawData)
        } catch (e) { xmpMeta = new XMPMeta() }

        XMPMeta.registerNamespace(customNamespace, prefix)
        xmpMeta.setProperty(customNamespace, key, value)
        layerObj.xmpMetadata.rawData = xmpMeta.serialize()
        return ""
    }

    this.get = function (layerObj, key) {
        try {
            xmpMeta = new XMPMeta(layerObj.xmpMetadata.rawData)
            var data = xmpMeta.getProperty(customNamespace, key)
        } catch (e) { }
        return data == undefined ? "" : data.value
    }
}

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'));
        return executeActionGet(r).hasKey(property)
    }

    this.convertToObject = function (obj, key) {
        var d = new ActionDescriptor();
        switch (obj.typename) {
            case 'ActionList': d.putList(s2t(key), obj); break;
            case 'ActionDescriptor': d = obj; break;
        }
        (desc = new ActionDescriptor()).putObject(s2t('object'), s2t('json'), d);
        eval('var o = ' + executeAction(s2t('convertJSONdescriptor'), desc).getString(s2t('json')));
        return o[key]
    }

    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;
        };
    }
}

 

Script 2: Get pattern scale from layer metadata.jsx

 

#target photoshop

var lr = new AM('layer'),
    meta = new LayerMetadata('pattern', 'scale');

if (lr.hasProperty('adjustment')) {
    var adj = lr.convertToObject(lr.getProperty(p = 'adjustment'), p)
    for (o in adj) {
        if (adj[o]._obj == 'patternLayer') {
            var scale = meta.get(activeDocument.activeLayer, 'fill')
            if (scale) {
                $.writeln(scale)
                lr.setAdjustmentFillScale(lr.getProperty(p), Number(scale))
            }
            break;
        }
    }
}

if (lr.hasProperty('layerEffects')) {
    var fx = lr.convertToObject(lr.getProperty(p = 'layerEffects').value, 'patternFill')
    if (fx) {
        var scale = meta.get(activeDocument.activeLayer, 'fx')
        if (scale) {
            $.writeln(scale)
            lr.setFxFillScale(lr.getProperty(p).value, Number(scale))
        }

    }
}

function LayerMetadata(customNamespace, prefix) {
    if (ExternalObject.AdobeXMPScript == undefined) ExternalObject.AdobeXMPScript = new ExternalObject('lib:AdobeXMPScript')

    this.set = function (layerObj, key, value) {
        try {
            xmpMeta = new XMPMeta(layerObj.xmpMetadata.rawData)
        } catch (e) { xmpMeta = new XMPMeta() }

        XMPMeta.registerNamespace(customNamespace, prefix)
        xmpMeta.setProperty(customNamespace, key, value)
        layerObj.xmpMetadata.rawData = xmpMeta.serialize()
    }

    this.get = function (layerObj, key) {
        try {
            xmpMeta = new XMPMeta(layerObj.xmpMetadata.rawData)
            var data = xmpMeta.getProperty(customNamespace, key)
        } catch (e) { }
        return data == undefined ? "" : data.value

    }
}

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'));
        return executeActionGet(r).hasKey(property)
    }

    this.setAdjustmentFillScale = function (list, scale) {
        for (var i = 0; i < list.count; i++) {
            if (t2s(list.getObjectType(i)) == 'patternLayer') {
                var fill = list.getObjectValue(i)
                fill.putUnitDouble(s2t('scale'), s2t('percentUnit'), scale)
            }
        }
        (r = new ActionReference()).putEnumerated(s2t("contentLayer"), s2t("ordinal"), s2t("targetEnum"));
        (d = new ActionDescriptor()).putReference(s2t("null"), r);
        d.putObject(s2t("to"), s2t("patternLayer"), fill);
        executeAction(s2t("set"), d, DialogModes.NO);

    }

    this.setFxFillScale = function (desc, scale) {
        s2t = stringIDToTypeID;

        var fill = desc.getObjectValue(s2t("patternFill"));
        fill.putUnitDouble(s2t('scale'), s2t('percentUnit'), scale);

        (r = new ActionReference()).putProperty(s2t("property"), p = s2t("layerEffects"));
        r.putEnumerated(s2t("layer"), s2t("ordinal"), s2t("targetEnum"));
        var fx = executeActionGet(r).getObjectValue(p);
        fx.putObject(s2t("patternFill"), s2t("patternFill"), fill);
        (d = new ActionDescriptor()).putReference(s2t("null"), r);
        d.putObject(s2t("to"), s2t("layerEffects"), fx);
        executeAction(s2t("set"), d, DialogModes.NO);
    }

    this.convertToObject = function (obj, key) {
        var d = new ActionDescriptor();
        switch (obj.typename) {
            case 'ActionList': d.putList(s2t(key), obj); break;
            case 'ActionDescriptor': d = obj; break;
        }
        (desc = new ActionDescriptor()).putObject(s2t('object'), s2t('json'), d);
        eval('var o = ' + executeAction(s2t('convertJSONdescriptor'), desc).getString(s2t('json')));
        return o[key]
    }

    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;
        };
    }
}

 

Kukurykus
Legend
August 15, 2021

To Apply Style 'Style 1', you choose it from Styles panel? Then on your screenshot you Set Current Fill Layer, but to do it first it's needed to make Adjustement Pattern Layer?

Legend
August 15, 2021

Yes. It is assumed that the user does not create, but changes the pattern. That is, all the necessary layers have already been created and configured. At least that's how I understood @32621919 

32621919Author
Participating Frequently
August 15, 2021

It could also be for just a pattern fill layer, it doesn't have to be a layer style...

Again, the problem with actions is the scale is a set value that will be a constant. And I need to the scale to change between each PSD since it's a different value.

JJMack
Community Expert
Community Expert
August 15, 2021

Are all you patterns created form the same size canvas  and have design that have object with similar number of pixels.   If your patters do not have the same Aspect Ration  no mater what percent scale you set the the patterns will tile differently.  

 

A script cans get a layers bounds to the know a layers location and a get the document bounds and document print resolution with ease.  With Action manager code a  script can mostly likely get the size of a pattern.  With this information you may be able to calculate some scale percentage  for the pattern to fill the layer so the Paters scaled the way you want it scaled for a particulate document canvas size and print resolution.

JJMack
32621919Author
Participating Frequently
August 15, 2021

All of the patterns have the same aspect ratio and pixel sizing.The only difference between the patterns is in colour. 
When you change a patterns fill in Photoshop by clicking another pattern, all it does is change the 'Name' and 'ID' variables and doesn't touch the scale. This is exactly what I want the script to do since the action is incapable of this.