Skip to main content
Known Participant
February 13, 2026
Question

PathItem subPathItems missing geometric data for visually separate shape islands (Photoshop scripting / ExtendScript)

  • February 13, 2026
  • 2 replies
  • 91 views

My setup

 

  • photoshopVersion: 26.9.0
  • scriptingVersion: 26.9
  • ExtendScript


I have some shape layers that were created with the pen tool (I think).

 

Now, I was trying to inspect the points in the subpath items of the path item associated with this shape layer. 

 

To access the path item associated with this shape layer, I can (apparently) do it 2 ways, but in both ways I get the problem I’ll describe below.

 

The first way is just to select “pathItem = myActiveDoc.pathItems[0]”, which you could argue is not reliable as there can be multiple paths, but bear with me and assume I don’t have any other path and have only these shape layers. The second way is to create a temporary path item from the shape layer’s vector mask using the action manager.

 

Anyway, after that, I iterate the “subPathItems” of “pathItem”, i.e. “subPathItem0 = pathItem.subPathItems[0]” and “subPathItem1 = pathItem.subPathItems[1]”. In my case, there are only 2 subpath items. 

 

Now, it seems that “subPathItem0” has only 1 point, i.e. “subPathItem0.pathPoints.length === 1”, while subPathItem1 has 29 points, which means that this first subpath is degenerate, as far as I understand. 

 

However, in photoshop, I see 2 big apparently isolated shapes in the same shape layer, so I expect to get at least 2 subpath items, which are not degenerate.

 

From what I can see, this issue often or always occurs when we have a shape layer where we have visually isolated shapes (islands). However, in some cases, some visual island are included in one subpath item’s points.

 

Has anyone ever experienced this? Is there a way to get all these shapes as vector paths?

 

If I export these shape layers as SVGs (i.e. right-click on the shape layer and then “Copy SVG”), I do see the correct shapes. So, this feature is working correctly, but apparently the subpath items don’t contain all info.

 

I actually don’t know if the different shapes I see in the shape layer should be different path items or different subpath items on the same path item. But in Photoshop, if I select the shape layer I’m interested in, in the Panels tab, I see only one path. If I delete that path, all the shapes are deleted. So, I’d assume that the shapes should be encoded into the subpath items for 1 path item, not in multiple path item.

 

Maybe I’m missing some precondition or something or need to collect the rest of the data from somewhere else. I really have no idea. I couldn’t find any other info so far.

    2 replies

    c.pfaffenbichler
    Community Expert
    Community Expert
    February 14, 2026

    The first way is just to select “pathItem = myActiveDoc.pathItems[0]”, which you could argue is not reliable as there can be multiple paths, but bear with me and assume I don’t have any other path and have only these shape layers. The second way is to create a temporary path item from the shape layer’s vector mask using the action manager.

    AM code should be able assess the Vector Mask itself, creating a temporary path seems unnecessary. 

    dev102Author
    Known Participant
    February 16, 2026

    I need to access the geometric information, i.e. the Bezier curves, so I need to access the PathPoints in the PathItems. 

     

    Can I get this information directly from the mask? If yes, how would you do that in ExtendScript?

    c.pfaffenbichler
    Community Expert
    Community Expert
    February 16, 2026
    // 2026, use at your own risk;
    if (app.documents.length > 0) {
    if (hasVectorMask() == true) {
    var theStuff = collectPathInfoFromDesc2023(activeDocument, activeDocument.pathItems.length);
    alert (theStuff.length+"\n\n"+theStuff.join("\n\n"));
    }
    };
    ////// collect path info from actiondescriptor, indices start at 1, not 0 //////
    function collectPathInfoFromDesc2023 (myDocument, thePath) {
    var originalRulerUnits = app.preferences.rulerUnits;
    app.preferences.rulerUnits = Units.POINTS;
    // based of functions from xbytor’s stdlib;
    var idPath = charIDToTypeID( "Path" );
    var ref = new ActionReference();
    // check if thePath is an index or a dom-path;
    if (thePath.constructor == Number) {
    ref.putIndex(idPath, thePath)
    }
    else {
    thePath.select();
    var ref = new ActionReference();
    ref.putEnumerated( charIDToTypeID( "Path" ), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
    };
    // get stuff;
    var desc = app.executeActionGet(ref);
    var pname = desc.getString(cTID('PthN'));
    // create new array;
    var theArray = new Array;
    var pathComponents = desc.getObjectValue(cTID("PthC")).getList(sTID('pathComponents'));
    // for subpathitems;
    for (var m = 0; m < pathComponents.count; m++) {
    var listKey = pathComponents.getObjectValue(m).getList(sTID("subpathListKey"));
    var operation1 = pathComponents.getObjectValue(m).getEnumerationValue(sTID("shapeOperation"));
    switch (operation1) {
    case 1097098272:
    var operation = 1097098272 //cTID('Add ');
    break;
    case 1398961266:
    var operation = 1398961266 //cTID('Sbtr');
    break;
    case 1231975538:
    var operation = 1231975538 //cTID('Intr');
    break;
    default:
    // case 1102:
    var operation = sTID('xor') //ShapeOperation.SHAPEXOR;
    break;
    };
    // for subpathitem’s count;
    for (var n = 0; n < listKey.count; n++) {
    theArray.push(new Array);
    var points = listKey.getObjectValue(n).getList(sTID('points'));
    try {var closed = listKey.getObjectValue(n).getBoolean(sTID("closedSubpath"))}
    catch (e) {var closed = false};
    // for subpathitem’s segment’s number of points;
    for (var o = 0; o < points.count; o++) {
    var anchorObj = points.getObjectValue(o).getObjectValue(sTID("anchor"));
    var anchor = [anchorObj.getUnitDoubleValue(sTID('horizontal')), anchorObj.getUnitDoubleValue(sTID('vertical'))];
    var thisPoint = [anchor];
    try {
    var left = points.getObjectValue(o).getObjectValue(cTID("Fwd "));
    var leftDirection = [left.getUnitDoubleValue(sTID('horizontal')), left.getUnitDoubleValue(sTID('vertical'))];
    thisPoint.push(leftDirection)
    }
    catch (e) {
    thisPoint.push(anchor)
    };
    try {
    var right = points.getObjectValue(o).getObjectValue(cTID("Bwd "));
    var rightDirection = [right.getUnitDoubleValue(sTID('horizontal')), right.getUnitDoubleValue(sTID('vertical'))];
    thisPoint.push(rightDirection)
    }
    catch (e) {
    thisPoint.push(anchor)
    };
    try {
    var smoothOr = points.getObjectValue(o).getBoolean(cTID("Smoo"));
    thisPoint.push(smoothOr)
    }
    catch (e) {thisPoint.push(false)};
    theArray[theArray.length - 1].push(thisPoint);
    };
    theArray[theArray.length - 1].push(closed);
    theArray[theArray.length - 1].push(operation);
    };
    };
    // by xbytor, thanks to him;
    function cTID (s) { return cTID[s] || cTID[s] = app.charIDToTypeID(s); };
    function sTID (s) { return sTID[s] || sTID[s] = app.stringIDToTypeID(s); };
    // reset;
    app.preferences.rulerUnits = originalRulerUnits;
    return theArray;
    };
    ////// from »Flatten All Masks.jsx« by jeffrey tranberry //////
    function hasVectorMask() {
    var hasVectorMask = false;
    try {
    var ref = new ActionReference();
    var keyVectorMaskEnabled = app.stringIDToTypeID( 'vectorMask' );
    var keyKind = app.charIDToTypeID( 'Knd ' );
    ref.putEnumerated( app.charIDToTypeID( 'Path' ), app.charIDToTypeID( 'Ordn' ), keyVectorMaskEnabled );
    var desc = executeActionGet( ref );
    if ( desc.hasKey( keyKind ) ) {
    var kindValue = desc.getEnumerationValue( keyKind );
    if (kindValue == keyVectorMaskEnabled) {
    hasVectorMask = true;
    }
    }
    }catch(e) {
    hasVectorMask = false;
    }
    return hasVectorMask;
    };

    You can give this a try. 

    c.pfaffenbichler
    Community Expert
    Community Expert
    February 14, 2026

    Please provide a file with the path. (A white jpg with the Path would suffice.) 

     

    A one-point-path seems perfectly possible. 

     

    dev102Author
    Known Participant
    February 16, 2026

    The shapes I have to deal with are more complex. When I click on them with the “Path Selection Tool”, I see several points.

     

    Moreover, like I said, the shape layers usually contain apparently isolated shapes (e.g. the head and the arm of a person, which are 2 “shape islands”).

     

    I didn’t know this, but apparently the Photoshop UI allows us to merge “shape components” with the “Path Selection Tool”. I noticed that, if I merge components, this seems to affect the data we can retrieve from the PathItem, but this alone doesn’t seem to solve the problem, i.e. we still don’t get all geometric data (of the shapes we see) from the path associated with the mask or shape layer - in my case, I get e.g. the data from another shape island (e.g. now I get the data for the head and before I was getting the data for the arm), which is really annoying, but it tells us that maybe Photoshop has a bug or maybe we need to do something else to get all data. I also came across this https://ai-scripting.docsforadobe.dev/jsobjref/CompoundPathItem/, but this is for Illustrator.

     

    I am working with proprietary assets, I don’t think I can share them, I’ll see if I can create something simpler to reproduce it, probably not, because I don’t know why this is happening in the first place. You could try to create more complex shapes and more importantly have at least 2 separate or isolated shapes in the same layer and see if you also observe this behaviour.

     

    It’s so annoying because the “Copy SVG” feature seems to work well (i.e. it gets the geometric data for all shapes, as far as I can see), so Photoshop definitely knows how to do it under the hood.

    c.pfaffenbichler
    Community Expert
    Community Expert
    February 16, 2026

    DOM code handles the subPathItems that result from »Merge Shape Components« badly, AM code should handle them just fine. 

    Please provide an image with an affected sample Path.