Copy link to clipboard
Copied
I'm using scripting (Javascript) to automate batch operations with Photoshop. I am creating patterns using "Define pattern" (this part is working).
However, I have been unable to figure out how to, from a script, create a fill pattern layer with that defined pattern and have it apply to the layer beneath (which is the top layer of the psd file).
Do folks have experience with this, or are you able to figure out a solution? The scripting reference is very... challenging... so I've been using GPT-4 to try to generate code for this, but nothing has worked.
The previous code was a standard pattern fill, I missed that you mentioned a pattern fill layer, so here is the code:
makePatternFillLayer("Tree Tile 4", "92150a0d-bc3e-9349-adc8-869fb042a4b0");
function makePatternFillLayer(patternName, patternID) {
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
var descriptor2 = new ActionDescriptor();
var descriptor3 = new ActionDescriptor();
var descriptor4 = new Actio
...
Copy link to clipboard
Copied
In the 2020 documentation, which is the latest I'm able to fine, it describes being able to set the "kind" for a new empty layer that you've created. However, when I do this in Photoshop 2023, it throws an error, saying that "kind" can only be set to text or normal, which seems like a pretty ruthless limitation:
Error 8193: You can only change the layer's kind to text or normal.
Copy link to clipboard
Copied
Wait a sec... is what happened is that Adobe nerfed the scripting capabilities in order to force people into using the developer cloud APIs and paying for them?
Copy link to clipboard
Copied
No need for wild conspiracy theories... Photoshop's DOM simply doesn't cover every single feature of Photoshop. Many operations require the use of Action Manger (AM) code, while some other things are simply not possible. For many, the most accessible way to get AM code is to record various operations via the ScriptingListener plug-in.
Here is an example of the raw output:
var idfill = stringIDToTypeID( "fill" );
var desc250 = new ActionDescriptor();
var idusing = stringIDToTypeID( "using" );
var idfillContents = stringIDToTypeID( "fillContents" );
var idpattern = stringIDToTypeID( "pattern" );
desc250.putEnumerated( idusing, idfillContents, idpattern );
var idpattern = stringIDToTypeID( "pattern" );
var desc251 = new ActionDescriptor();
var idname = stringIDToTypeID( "name" );
desc251.putString( idname, """Tree Tile 4""" );
var idID = stringIDToTypeID( "ID" );
desc251.putString( idID, """92150a0d-bc3e-9349-adc8-869fb042a4b0""" );
var idpattern = stringIDToTypeID( "pattern" );
desc250.putObject( idpattern, idpattern, desc251 );
var idopacity = stringIDToTypeID( "opacity" );
var idpercentUnit = stringIDToTypeID( "percentUnit" );
desc250.putUnitDouble( idopacity, idpercentUnit, 100.000000 );
var idmode = stringIDToTypeID( "mode" );
var idblendMode = stringIDToTypeID( "blendMode" );
var idnormal = stringIDToTypeID( "normal" );
desc250.putEnumerated( idmode, idblendMode, idnormal );
executeAction( idfill, desc250, DialogModes.NO );
The following code has been "cleaned" and put into a function with parameters put into the function call.
patternFill("Tree Tile 4", "92150a0d-bc3e-9349-adc8-869fb042a4b0");
function patternFill(patternName, patternID) {
function s2t(s) {
return app.stringIDToTypeID(s);
}
var descriptor = new ActionDescriptor();
var descriptor2 = new ActionDescriptor();
descriptor.putEnumerated( s2t( "using" ), s2t( "fillContents" ), s2t( "pattern" ));
descriptor2.putString( s2t( "name" ), patternName );
descriptor2.putString( s2t( "ID" ), patternID );
descriptor.putObject( s2t( "pattern" ), s2t( "pattern" ), descriptor2 );
descriptor.putUnitDouble( s2t( "opacity" ), s2t( "percentUnit" ), 100.000000 );
descriptor.putEnumerated( s2t( "mode" ), s2t( "blendMode" ), s2t( "normal" ));
executeAction( s2t( "fill" ), descriptor, DialogModes.NO );
}
Copy link to clipboard
Copied
The previous code was a standard pattern fill, I missed that you mentioned a pattern fill layer, so here is the code:
makePatternFillLayer("Tree Tile 4", "92150a0d-bc3e-9349-adc8-869fb042a4b0");
function makePatternFillLayer(patternName, patternID) {
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
var descriptor2 = new ActionDescriptor();
var descriptor3 = new ActionDescriptor();
var descriptor4 = new ActionDescriptor();
var reference = new ActionReference();
reference.putClass( s2t( "contentLayer" ));
descriptor.putReference( s2t( "null" ), reference );
descriptor4.putString( s2t( "name" ), patternName );
descriptor4.putString( s2t( "ID" ), patternID );
descriptor3.putObject( s2t( "pattern" ), s2t( "pattern" ), descriptor4 );
descriptor2.putObject( s2t( "type" ), s2t( "patternLayer" ), descriptor3 );
descriptor.putObject( s2t( "using" ), s2t( "contentLayer" ), descriptor2 );
executeAction( s2t( "make" ), descriptor, DialogModes.NO );
}
Copy link to clipboard
Copied
As I'm automating this pattern generation, I can make the "name" for the pattern, but I'm not sure what's required for the ID. I assume I can't reuse the same serial number; is there some generator I should be using in my script?
Thank you very much for the help!
Copy link to clipboard
Copied
Haha I need to be able to delete posts. I got confused because I ended up taking a totally different route because I wasn't able to get this working last weekend. Let me sort my sanity out and try this. Thanks!
Copy link to clipboard
Copied
Works great! Turns out the PatternID is not a required property to get passed in, so I was able to just do:
makePatternFillLayer("Tree Tile 4");
function makePatternFillLayer(patternName) {
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
var descriptor2 = new ActionDescriptor();
var descriptor3 = new ActionDescriptor();
var descriptor4 = new ActionDescriptor();
var reference = new ActionReference();
reference.putClass( s2t( "contentLayer" ));
descriptor.putReference( s2t( "null" ), reference );
descriptor4.putString( s2t( "name" ), patternName );
descriptor3.putObject( s2t( "pattern" ), s2t( "pattern" ), descriptor4 );
descriptor2.putObject( s2t( "type" ), s2t( "patternLayer" ), descriptor3 );
descriptor.putObject( s2t( "using" ), s2t( "contentLayer" ), descriptor2 );
executeAction( s2t( "make" ), descriptor, DialogModes.NO );
}
Which is good, since getting the PatternID was going to be yet another mini-odyssey for this newbie.
Thank you also for the link to ScriptingListener, that's a nice packager.
Copy link to clipboard
Copied
Ah, I meant "cleaner".
Copy link to clipboard
Copied