Skip to main content
Participant
April 12, 2024
Answered

How to fill a path in Adobe Photoshop with ExtendScript?

  • April 12, 2024
  • 2 replies
  • 1152 views

I have a function that I used to draw lines and then stroke the path.

 

export const getSolidColor = (hex) => {
    const color = new SolidColor()
    color.rgb.hexValue = hex
    return color
}

export const drawLines = (doc, coordinates, title) => {
    const lines = []
    for (i = 0; i < coordinates.length; i++) {
        const { anchor, kind, ldr, rdr } = coordinates[i]
        const point = [anchor.x, anchor.y]
        const line = new PathPointInfo()
        line.kind = kind
        line.anchor = point
        line.leftDirection = ldr ? [ldr.x, ldr.y] : point
        line.rightDirection = rdr ? [rdr.x, rdr.y] : point
        lines.push(line)
    }

    const subPath = new SubPathInfo()
    subPath.closed = true
    subPath.entireSubPath = lines
    subPath.operation = ShapeOperation.SHAPEXOR

    const item = doc.pathItems.add(title, [subPath])
    item.deselect()
    return item
}

When I call this function to strokePath it works fine.

// Create a new document
const width = 1920
const height = 1080
const ppi = 72
const title = "Character 1"
const mode = NewDocumentMode.RGB
const newDoc = app.documents.add(width, height, ppi, title, mode)

const brush = ToolType.BRUSH
drawLines(newDoc, coords, "Line 1").strokePath(brush)

 

But, when I try to fill the path using the fillPath method, the layer just appears empty.

 

export const convertPoints = (points) => {
    const newPoints = []
    for (i = 0; i < points.length; i++) {
        newPoints.push({
            kind: PointKind.CORNERPOINT,
            anchor: {
                x: points[i][0],
                y: points[i][1]
            },
            ldr: null,
            rdr: null
        })
    }
    return newPoints
}

const newPoints = convertPoints([
    [width * 0.33, height * 0],
    [width * 0.33, height * 1],
    [width * 0.5, height * 1],
    [width * 0.5, height * 0]
])
const fillColor = getSolidColor("0080ff")
const opacity = 90
const fillMode = ColorBlendMode.NORMAL
const preserveTransparency = true
const feather = 0
const wholePath = true
const antiAlias = true
drawLines(newDoc, newPoints, "Line 2").fillPath(
    fillColor,
    fillMode,
    opacity,
    preserveTransparency,
    feather,
    wholePath,
    antiAlias
)

The shape appears along with the anchor points on the Photoshop document but its not colored or filled inside. Does anyone know how to do with with ExtendScript?

Thank you!

This topic has been closed for replies.
Correct answer r-bin

Your code is definitely not for ExtendScript. Perhaps this is for UXP, which is not available to me.

Try replacing

const preserveTransparency = true

with

const preserveTransparency = false

2 replies

c.pfaffenbichler
Community Expert
Community Expert
April 13, 2024

Could you please post screenshots with the pertinent Panels (Toolbar, Layers, Paths, Options Bar, …) visible? 

 

How did you create the code? 

Why would you fill a Path instead of creating a Shape Layer? 

Chuck Uebele
Community Expert
Community Expert
April 14, 2024
I've never been able to make a shape layer using extendscript. I've always had to create the path first, then convert it to a shape layer. Do you know a way to directly make a shape layer? 
 
quote

Why would you fill a Path instead of creating a Shape Layer? 


By @c.pfaffenbichler

 

c.pfaffenbichler
Community Expert
Community Expert
April 14, 2024

I've always had to create the path first, then convert it to a shape layer. Do you know a way to directly make a shape layer? 

Do you mean with the Pen Tool (or other Path-releated Tools) or via Script? 

Sorry, I had read sloppily. 

 

With a Script that would need a selected Path or one could create a Work Path before creating the Shape Layer, maybe other options exist. 

This Script would create a Shape Layer of two circles. 

 

// 2024, use it at your own risk;
if (app.documents.length > 0) {
var myDocument = app.activeDocument;
var theArray = [[[[123.946875,67.756875],[153.647014898625,67.756875],[94.246735101375,67.756875],true],[[177.72375,121.53375],[177.72375,151.233889898625],[177.72375,91.833610101375],true],
[[123.946875,175.310625],[94.246735101375,175.310625],[153.647014898625,175.310625],true],[[70.17,121.53375],[70.17,91.833610101375],[70.17,151.233889898625],true],true,1097098272],
[[[173.3953125,133.648125],[194.627376180187,133.648125],[152.163248819812,133.648125],true],[[211.839375,172.0921875],[211.839375,193.324251180188],[211.839375,150.860123819812],true],
[[173.3953125,210.53625],[152.163248819812,210.53625],[194.627376180187,210.53625],true],[[134.95125,172.0921875],[134.95125,150.860123819812],[134.95125,193.324251180188],true],true,1737]];
var theX = createShapeLayer(theArray, [0,125,255], 30, [0,0,0]);
};
////////////////////////////////////
////// create a solid color layer //////
function createShapeLayer(theArray, fillColor, strokeWidth, strokeColor) {
var originalRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
// thanks to xbytor;
cTID = function(s) { return app.charIDToTypeID(s); };
sTID = function(s) { return app.stringIDToTypeID(s); };


    var desc1 = new ActionDescriptor();
    var ref1 = new ActionReference();
    ref1.putProperty(cTID('Path'), cTID('WrPt'));
    desc1.putReference(sTID('null'), ref1);
    var list1 = new ActionList();

for (var m = 0; m < theArray.length; m++) {
var thisSubPath = theArray[m];

    var desc2 = new ActionDescriptor();
    desc2.putEnumerated(sTID('shapeOperation'), sTID('shapeOperation'), 1908);
//        desc2.putEnumerated(sTID('shapeOperation'), sTID('shapeOperation'), thisSubPath[thisSubPath.length - 1]);
    var list2 = new ActionList();
    var desc3 = new ActionDescriptor();
    desc3.putBoolean(cTID('Clsp'), thisSubPath[thisSubPath.length - 2]);
    var list3 = new ActionList();

for (var n = 0; n < thisSubPath.length - 2; n++) {
var thisPoint = thisSubPath[n];

    var desc4 = new ActionDescriptor();
    var desc5 = new ActionDescriptor();
    desc5.putUnitDouble(cTID('Hrzn'), cTID('#Rlt'), thisPoint[0][0]);
    desc5.putUnitDouble(cTID('Vrtc'), cTID('#Rlt'), thisPoint[0][1]);
    desc4.putObject(cTID('Anch'), cTID('Pnt '), desc5);
    var desc6 = new ActionDescriptor();
    desc6.putUnitDouble(cTID('Hrzn'), cTID('#Rlt'), thisPoint[1][0]);
    desc6.putUnitDouble(cTID('Vrtc'), cTID('#Rlt'), thisPoint[1][1]);
    desc4.putObject(cTID('Fwd '), cTID('Pnt '), desc6);
    var desc7 = new ActionDescriptor();
    desc7.putUnitDouble(cTID('Hrzn'), cTID('#Rlt'), thisPoint[2][0]);
    desc7.putUnitDouble(cTID('Vrtc'), cTID('#Rlt'), thisPoint[2][1]);
    desc4.putObject(cTID('Bwd '), cTID('Pnt '), desc7);
    desc4.putBoolean(cTID('Smoo'), thisPoint[3]);
    list3.putObject(cTID('Pthp'), desc4);

};

    desc3.putList(cTID('Pts '), list3);
    list2.putObject(cTID('Sbpl'), desc3);
    desc2.putList(cTID('SbpL'), list2);
    list1.putObject(cTID('PaCm'), desc2);
};

    desc1.putList(cTID('T   '), list1);
    executeAction(cTID('setd'), desc1, DialogModes.NO);
// solid color layer;
    var desc16 = new ActionDescriptor();
        var ref4 = new ActionReference();
        ref4.putClass( stringIDToTypeID( "contentLayer" ) );
    desc16.putReference( charIDToTypeID( "null" ), ref4 );
        var desc17 = new ActionDescriptor();
            var desc18 = new ActionDescriptor();
            if (fillColor != false) {
                var desc19 = new ActionDescriptor();
                desc19.putDouble( charIDToTypeID( "Rd  " ), fillColor[0] );
                desc19.putDouble( charIDToTypeID( "Grn " ), fillColor[1] );
                desc19.putDouble( charIDToTypeID( "Bl  " ), fillColor[2] );
            desc18.putObject( charIDToTypeID( "Clr " ), charIDToTypeID( "RGBC" ), desc19 );
            };
            desc17.putObject( charIDToTypeID( "Type" ), stringIDToTypeID( "solidColorLayer" ), desc18 );
////////////////////////////////////
            var desc38 = new ActionDescriptor();
            desc38.putInteger( stringIDToTypeID( "strokeStyleVersion" ), 2 );
            if (strokeColor != false) {desc38.putBoolean( stringIDToTypeID( "strokeEnabled" ), true )}
            else {desc38.putBoolean( stringIDToTypeID( "strokeEnabled" ), false )};
            if (fillColor == false) {desc38.putBoolean( stringIDToTypeID( "fillEnabled" ), false )};
            desc38.putUnitDouble( stringIDToTypeID( "strokeStyleLineWidth" ), stringIDToTypeID( "pixelsUnit" ), strokeWidth );
            desc38.putUnitDouble( stringIDToTypeID( "strokeStyleLineDashOffset" ), stringIDToTypeID( "pointsUnit" ), 0.000000 );
            desc38.putDouble( stringIDToTypeID( "strokeStyleMiterLimit" ), 100.000000 );
            desc38.putEnumerated( stringIDToTypeID( "strokeStyleLineCapType" ), stringIDToTypeID( "strokeStyleLineCapType" ), stringIDToTypeID( "strokeStyleButtCap" ) );
            desc38.putEnumerated( stringIDToTypeID( "strokeStyleLineJoinType" ), stringIDToTypeID( "strokeStyleLineJoinType" ), stringIDToTypeID( "strokeStyleMiterJoin" ) );
            desc38.putEnumerated( stringIDToTypeID( "strokeStyleLineAlignment" ), stringIDToTypeID( "strokeStyleLineAlignment" ), stringIDToTypeID( "strokeStyleAlignCenter" ) );
            var idstrokeStyleScaleLock = stringIDToTypeID( "strokeStyleScaleLock" );
            desc38.putBoolean( idstrokeStyleScaleLock, false );
            desc38.putBoolean( stringIDToTypeID( "strokeStyleStrokeAdjust" ), false );
                var list5 = new ActionList();
            desc38.putList( stringIDToTypeID( "strokeStyleLineDashSet" ), list5 );
            desc38.putEnumerated( stringIDToTypeID( "strokeStyleBlendMode" ), stringIDToTypeID( "blendMode" ), stringIDToTypeID( "normal" ) );
            desc38.putUnitDouble( stringIDToTypeID( "strokeStyleOpacity" ), stringIDToTypeID( "percentUnit" ), 100.000000 );
                var desc39 = new ActionDescriptor();
                if (strokeColor != false) {
                    var desc40 = new ActionDescriptor();
                    desc40.putDouble( charIDToTypeID( "Rd  " ), strokeColor[0] );
                    desc40.putDouble( charIDToTypeID( "Grn " ), strokeColor[1] );
                    desc40.putDouble( charIDToTypeID( "Bl  " ), strokeColor[2] );
                desc39.putObject( charIDToTypeID( "Clr " ), charIDToTypeID( "RGBC" ), desc40 );
                };
            desc38.putObject( stringIDToTypeID( "strokeStyleContent" ), stringIDToTypeID( "solidColorLayer"), desc39 );
            desc38.putDouble( stringIDToTypeID( "strokeStyleResolution" ), 300.000000 );
            desc17.putObject( stringIDToTypeID( "strokeStyle" ), stringIDToTypeID( "strokeStyle" ), desc38 );
////////////////////////////////////
    desc16.putObject( charIDToTypeID( "Usng" ), stringIDToTypeID( "contentLayer" ), desc17 );
executeAction( charIDToTypeID( "Mk  " ), desc16, DialogModes.NO );
app.preferences.rulerUnits = originalRulerUnits;
// get identifier;
var ref = new ActionReference();
ref.putProperty (stringIDToTypeID ("property"), stringIDToTypeID ("layerID"));
ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
return executeActionGet(ref).getInteger(stringIDToTypeID("layerID"));
};

 

 

r-binCorrect answer
Legend
April 12, 2024

Your code is definitely not for ExtendScript. Perhaps this is for UXP, which is not available to me.

Try replacing

const preserveTransparency = true

with

const preserveTransparency = false

Participant
April 12, 2024

It's standard JavaScript. Once it gets compiled it makes more sense. 

Legend
April 13, 2024
quote

It's standard JavaScript. Once it gets compiled it makes more sense. 


By @Lance31522690av1y

JavaScript and ExtendScript are not the same thing.

Your code does not work, for example, in PS2020 due to the fact that it is not ExtendScript.

 

P.S. Have you tried my advice?