Photoshop: How to get list of layer comps in smart object using a script?

  • 3
  • Question
  • Updated 8 months ago
  • Answered
  • (Edited)
Hard question. Chris Cox could know it.

1) How to get list of layer comps in smart object?
2) How to check which is active?
3) How to compare layer comps which is set in root document and comps which is inside smart object?

I tried use script listener.

This is change layer comps from root document.
// =======================================================
var idsetPlacedLayerComp = stringIDToTypeID( "setPlacedLayerComp" );
    var desc69 = new ActionDescriptor();
    var idnull = charIDToTypeID( "null" );
        var ref20 = new ActionReference();
        var idLyr = charIDToTypeID( "Lyr " );
        var idOrdn = charIDToTypeID( "Ordn" );
        var idTrgt = charIDToTypeID( "Trgt" );
        ref20.putEnumerated( idLyr, idOrdn, idTrgt );
    desc69.putReference( idnull, ref20 );
    var idcompID = stringIDToTypeID( "compID" );
    desc69.putInteger( idcompID, 986290194 );
executeAction( idsetPlacedLayerComp, desc69, DialogModes.NO );

This is how looks change comps inside smart object. I used layer comps panel instead properties panel in first example.
//======================================================= 
var idapplyComp = stringIDToTypeID( "applyComp" );
    var desc75 = new ActionDescriptor();
    var idnull = charIDToTypeID( "null" );
        var ref24 = new ActionReference();
        var idcompsClass = stringIDToTypeID( "compsClass" );
        ref24.putName( idcompsClass, "Kompozice vrstev 1" );
    desc75.putReference( idnull, ref24 );
executeAction( idapplyComp, desc75, DialogModes.NO );

How can I compare if 986290194 == "Kompozice vrstev 1" ?
How can I get full list of numers like 986290194 ?
I didn't found this in documentation.

I wan't make script like this: http://blog.darkwark.com/expandSmartObject/ but better. It's really, really important in my workflow.

Thanks for the answers
Photo of Jaroslav Bereza

Jaroslav Bereza

  • 785 Posts
  • 169 Reply Likes
  • lost in code

Posted 3 years ago

  • 3
Photo of Chris Cox

Chris Cox

  • 20280 Posts
  • 814 Reply Likes
Right now you can't get the list without opening the child document. And I don't think there is any way to get the currently selected comp information.

I'll have to see if we can expose the layer comp cache and current setting information from the parent document.
Photo of Jaroslav Bereza

Jaroslav Bereza

  • 785 Posts
  • 169 Reply Likes
Can I receive a bitmap of object smart from parent document?
Insert it into the child element?
Set blending mode to the difference.
Place it on the exact coordinates.
Switch composition inside the child document until the sum of all the RGB values is equal to zero?

How slow would it be?
I will alway have RGB and my Smart object dimensions will be always 1:1 — parent:child
Photo of Max Johnson

Max Johnson, Champion

  • 457 Posts
  • 212 Reply Likes
I had a similar question/problem and posted some script here... https://feedback.photoshop.com/photos...

I ended up using a custom generator plugin to get the doc info from smart objects
Photo of Jaroslav Bereza

Jaroslav Bereza

  • 785 Posts
  • 169 Reply Likes
So you can get current current comps id in child document from parrent document? I am a bit confused :-)
Photo of Chris Cox

Chris Cox

  • 20280 Posts
  • 814 Reply Likes
I think that is only working through Generator. I'll have to double check the code.
Photo of Max Johnson

Max Johnson, Champion

  • 457 Posts
  • 212 Reply Likes
Thank you Chris!
Photo of Chris Cox

Chris Cox

  • 20280 Posts
  • 814 Reply Likes
OK, Smart Object descriptor info does include the ID of the currently selected comp for the child document, which will be -1 if none are selected.

The list of comps known is not exposed anywhere but in the PSD file.  I'll see if I can add the list of comps (and other comp data) to the Smart Object Info for the next release.  It'll be read only -- the only data you can change is the current comp ID value.
Photo of Max Johnson

Max Johnson, Champion

  • 457 Posts
  • 212 Reply Likes
That would be amazing. Thank you!
Photo of christoph pfaffenbichler

christoph pfaffenbichler, Champion

  • 1228 Posts
  • 173 Reply Likes
»Smart Object descriptor info does include the ID of the currently selected comp for the child document«
Could you please elaborate how exactly can one access that information? 
I can discern 
• placed
• documentID
• compsList
• linked
• fileReference
• link
• linkMissing
• linkChanged
but none of them seems to indicate the currently applied LayerComp. 
(Edited)
Photo of christoph pfaffenbichler

christoph pfaffenbichler, Champion

  • 1228 Posts
  • 173 Reply Likes
I assume compsList should be where the info is but compID (the way I use it at least) seems to return nonsense with my test file – specifically »-1« even though a LayerComp is applied. 
http://transfer.viennapaint.com/index.php/s/ytdzVIpz1tVsdHW
Mr.Cox, could you please take the trouble to check where I am going wrong? 

if (app.documents.length > 0) {var ref = new ActionReference();
ref.putProperty (stringIDToTypeID ("property"), stringIDToTypeID ("smartObject"));
ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
var layerDesc = executeActionGet(ref);
var theSO = layerDesc.getObjectValue(stringIDToTypeID("smartObject"));
var xxx = theSO.getObjectValue(stringIDToTypeID("compsList")).getInteger(stringIDToTypeID("originalCompID"));
var yyy = theSO.getObjectValue(stringIDToTypeID("compsList")).getInteger(stringIDToTypeID("compID"));
alert ("compID "+yyy+"\noriginalCompID "+xxx);
};
Photo of Tom Ruark

Tom Ruark, Official Rep

  • 26 Posts
  • 11 Reply Likes
Looks correct. Are you not getting an originalCompID? In my tests it gives me a number that matches the list "compList" that is provided which is a list of descriptors containing keyName, keyID, and "comment"

Use the Getter plugin from the SDK to see everything.
Photo of christoph pfaffenbichler

christoph pfaffenbichler, Champion

  • 1228 Posts
  • 173 Reply Likes
Thanks for chiming in. 

I don’t have the current SDK but with the Script I get compID -1  for all the Smart Objects even though they all have a different LayerComp applied – should the compID not reflect the LayerComp applied? 
Photo of christoph pfaffenbichler

christoph pfaffenbichler, Champion

  • 1228 Posts
  • 173 Reply Likes
Thanks for pointing out the "compList" by the way. 
Photo of christoph pfaffenbichler

christoph pfaffenbichler, Champion

  • 1228 Posts
  • 173 Reply Likes
And for originalCompID I get 579586559 for all three SOs in my test file. 
Photo of Max Johnson

Max Johnson, Champion

  • 457 Posts
  • 212 Reply Likes
More test results for originalCompID: (SO refers to SmartObject)
1. all instances of a particular SO return the same originalCompID
2. *different* SO sources have different originalCompIDs from others, but again, all instances of each individual SO source have the same originalCompID.
3. If you open the SO, change the layer comp, then save it, the originalCompID in the master document will update to be the compID of the comp applied inside the smart object. It is still duplicated among all instances, however.
Photo of Max Johnson

Max Johnson, Champion

  • 457 Posts
  • 212 Reply Likes
More test results for originalCompID: (SO refers to SmartObject)
1. all instances of a particular SO return the same originalCompID
2. *different* SO sources have different originalCompIDs from others, but again, all instances of each individual SO source have the same originalCompID.
3. If you open the SO, change the layer comp, then save it, the originalCompID in the master document will update to be the compID of the comp applied inside the smart object. It is still duplicated among all instances, however.
Photo of christoph pfaffenbichler

christoph pfaffenbichler, Champion

  • 1228 Posts
  • 173 Reply Likes
I feared the fact that the SO in my original test file had been created prior to CC 2015 might be an issue but a wholly newly created file shows the same nonsensical results for compID. 
(the alert in the screenshot lists name, ID, compID and originalCompID for all Smart Object instances in the file)
 

What exactly is compID supposed to indicate? 
Photo of Jaroslav Bereza

Jaroslav Bereza

  • 785 Posts
  • 169 Reply Likes
Chris Cox
OK, Smart Object descriptor info does include the ID of the currently selected comp for the child document, which will be -1 if none are selected.
So... Will I be able compare if 986290194 == "Kompozice vrstev 1" ? Is there way how to convert comps names into id numbers like 986290194 ? If not, then I can't solve my script.

I need:
1) check which comps is applied on smart object in parent document
2) open child document
3) switch composition inside child document
4) copy content to parent document

I am looking for something like 
layerComps.getByID(986290194)
instead
 layerComps.getByName("Kompozice vrstev 1")
(Edited)
Photo of christoph pfaffenbichler

christoph pfaffenbichler, Champion

  • 1228 Posts
  • 173 Reply Likes
Were you able to determine the LayerComp (id or name, no matter) currently applied to a Smart Object’s instance? 
If so: How did you do that? 
Photo of christoph pfaffenbichler

christoph pfaffenbichler, Champion

  • 1228 Posts
  • 173 Reply Likes
Did not seem to help. 
In my testing the ID of the actually applied LayerComp proved elusive and I seemed to get the same one erroneously for all instances. 
Photo of Max Johnson

Max Johnson, Champion

  • 457 Posts
  • 212 Reply Likes
Same bere, but my tests were before that last update so I was going to retest Monday.
Photo of Max Johnson

Max Johnson, Champion

  • 457 Posts
  • 212 Reply Likes
Verified that the "compID" is always -1 and the "originalCompID" never changes, regardless of which comp has been applied to the smart object.
Photo of Jaroslav Bereza

Jaroslav Bereza

  • 785 Posts
  • 169 Reply Likes
With this: http://www.tonton-pixel.com/blog/scripts/utility-scripts/get-document-info-code/ I can see this:
"smartObject":
  {
    "<object>":
    {
      "smartObject":
      {
        "placed":
        {
          "<enumerated>":
          {
            "placed": "rasterizeContent"
          }
        },
        "documentID":
        {
          "<string>": "CEFB1ED3C949A7DACDD9635AF101DF4A"
        },
        "compsList":
        {
          "<object>":
          {
            "null":
            {
              "compID":
              {
                "<integer>": -1
              },
              "originalCompID":
              {
                "<integer>": 2117910372
              },
              "compList":
              {
                "<list>":
                [
                  {
                    "<object>":
                    {
                      "null":
                      {
                        "name":
                        {
                          "<string>": "jarda1"
                        },
                        "ID":
                        {
                          "<integer>": 2111826238
                        },
                        "comment":
                        {
                          "<string>": ""
                        }
                      }
                    }
                  },
                  {
                    "<object>":
                    {
                      "null":
                      {
                        "name":
                        {
                          "<string>": "jarda2"
                        },
                        "ID":
                        {
                          "<integer>": 2117910372
                        },
                        "comment":
                        {
                          "<string>": ""
                        }
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "linked":
        {
          "<boolean>": false
        },
        "fileReference":
        {
          "<string>": "Rectangle 1.psb"
        }
      }
    }
  },
Photo of christoph pfaffenbichler

christoph pfaffenbichler, Champion

  • 1228 Posts
  • 173 Reply Likes
But again the compID is -1 – has no LayerComp been applied to the SO? 
And if the written file itself is accessed it would only reflect changes if it has been saved since. 
Photo of Jaroslav Bereza

Jaroslav Bereza

  • 785 Posts
  • 169 Reply Likes
Comp "jarda2" is applied on SO. 
originalCompID is same for all instances and no value change if I change applied comp on SO from parent document.

What is difference in compID vs. originalCompID?
Photo of Max Johnson

Max Johnson, Champion

  • 457 Posts
  • 212 Reply Likes
compID should be the id of the comp applied in the master document to the instanced smart object, while originalCompID seems to be the comp ID of the comp that is *saved* as applied inside the the smart object itself.
Photo of christoph pfaffenbichler

christoph pfaffenbichler, Champion

  • 1228 Posts
  • 173 Reply Likes
»compID should be the id of the comp applied in the master document to the instanced smart object«
Unfortunately it is not in the current release (CC 2015.1.2) on my computer – so has there been a mistake in the implementation? 
Photo of Tom Ruark

Tom Ruark, Official Rep

  • 26 Posts
  • 11 Reply Likes
Try this script:

// Version 1.0.0.0// Result: Wed Apr 20 2016 13:26:24 GMT-0700

// Find out about 
//  The layer comps 
//  in the smart object 
//  of the current layer

var totalTime = new Timer();

var myLogging = new MyLogging(false /* Alert */, 
                              false /* WriteLine */, 
                              true /* Log File */, 
                              Folder.desktop + "/SmartObjectsAndLayerComps.txt");

if (documents.length === 0) {

    alert("Create a document, create a smart object layer and try again.");

} else {

    if (activeDocument.activeLayer.kind !== LayerKind.SMARTOBJECT) {
        
        alert("Create a smart object layer and try again.");

    } else {

        myLogging.LogIt("GetSmartObjectInfo: " + GetSmartObjectInfo());
        
    }

}

myLogging.execute();

'DONE';

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

function GetSmartObjectInfo() {
    if ( typeof classLayer == "undefined" )
        classLayer = app.charIDToTypeID('Lyr ');
    if ( typeof classProperty == "undefined" )
        classProperty = app.charIDToTypeID('Prpr');
    if ( typeof typeOrdinal == "undefined" )
        typeOrdinal = app.charIDToTypeID('Ordn');
    if ( typeof enumTarget == "undefined" )
        enumTarget = app.charIDToTypeID('Trgt');
        
    var ref = new ActionReference();
    ref.putEnumerated(classLayer, typeOrdinal, enumTarget);
    var desc = executeActionGet(ref);
    var anObj = new Object();
    DescriptorToObject(anObj, desc);
    return anObj["smartObject"].toSource();
}

///////////////////////////////////////////////////////////////////////////////
// Function: ObjectToFile
// Usage: Dump out a JavaScript object to File
// Input: JavaScript Object (o), current object to output
//        JavaScript File (f), file to append to
// Return: error, 0 means OK, -1 means could not open, positive means issues with
//         certain items in the object
// TODO: why can't I just toSource() the entire thing?
///////////////////////////////////////////////////////////////////////////////
function ObjectToFile(o, f) {
    var returnValue = 0;
    if (f.open('a')) {
        for (var i in o) { 
            if ( ! f.writeln(i + " : " + o[i].toSource())) {
                myLogging.LogIt("FAIL: Could not write: " + i + " : " + o[i].toSource());
                return
            }
        }
        f.writeln();
        f.close();
    } else {
        myLogging.LogIt("FAIL: Could not append to file.");
        returnValue = -1;
    }
    return returnValue;
}



///////////////////////////////////////////////////////////////////////////////
// Function: DescriptorToObject
// Usage: update a JavaScript Object from an ActionDescriptor
// Input: JavaScript Object (o), current object to update (output)
//  Photoshop ActionDescriptor (d), descriptor to pull new params for object from
//  JavaScript Function (f), post process converter utility to convert
// Return: Nothing, update is applied to passed in JavaScript Object (o)
///////////////////////////////////////////////////////////////////////////////
function DescriptorToObject(o, d, f) {
var l = d.count;
for (var i = 0; i < l; i++) {
var k = d.getKey(i);
var t = d.getType(k);
var strk = app.typeIDToStringID(k);
        if (strk.length == 0) {
            strk = app.typeIDToCharID(k);
        }
switch (t) {
case DescValueType.BOOLEANTYPE:
o[strk] = d.getBoolean(k);
break;
case DescValueType.STRINGTYPE:
o[strk] = d.getString(k);
break;
case DescValueType.DOUBLETYPE:
o[strk] = d.getDouble(k);
break;
case DescValueType.INTEGERTYPE:
                o[strk] = d.getInteger(k);
                break;
            case DescValueType.LARGEINTEGERTYPE:
            o[strk] = d.getLargeInteger(k);
            break;
case DescValueType.OBJECTTYPE:
                var newT = d.getObjectType(k);
                var newV = d.getObjectValue(k);
                o[strk] = new Object();
                DescriptorToObject(o[strk], newV, f);
                break;
case DescValueType.UNITDOUBLE:
                var newT = d.getUnitDoubleType(k);
                var newV = d.getUnitDoubleValue(k);
                o[strk] = new Object();
                o[strk].type = typeIDToCharID(newT);
                o[strk].typeString = typeIDToStringID(newT);
                o[strk].value = newV;
                break;
case DescValueType.ENUMERATEDTYPE:
                var newT = d.getEnumerationType(k);
                var newV = d.getEnumerationValue(k);
                o[strk] = new Object();
                o[strk].type = typeIDToCharID(newT);
                o[strk].typeString = typeIDToStringID(newT);
                o[strk].value = typeIDToCharID(newV);
                o[strk].valueString = typeIDToStringID(newV);
                break;
case DescValueType.CLASSTYPE:
                o[strk] = d.getClass(k);
                break;
case DescValueType.ALIASTYPE:
                o[strk] = d.getPath(k);
                break;
case DescValueType.RAWTYPE:
                var tempStr = d.getData(k);
                o[strk] = new Array();
                for (var tempi = 0; tempi < tempStr.length; tempi++) { 
                    o[strk][tempi] = tempStr.charCodeAt(tempi); 
                }
                break;
case DescValueType.REFERENCETYPE:
                var ref = d.getReference(k);
                o[strk] = new Object();
                ReferenceToObject(o[strk], ref, f);
                break;
case DescValueType.LISTTYPE:
                var list = d.getList(k);
                o[strk] = new Array();
                ListToObject(o[strk], list, f);
                break;
default:
myLogging.LogIt("Unsupported type in descriptorToObject " + t);
}
}
if (undefined != f) {
o = f(o);
}
}



///////////////////////////////////////////////////////////////////////////////
// Function: ListToObject
// Usage: update a JavaScript Object from an ActionList
// Input: JavaScript Array (a), current array to update (output)
//        Photoshop ActionList (l), list to pull new params for object from
//        JavaScript Function (f), post process converter utility to convert
// Return: Nothing, update is applied to passed in JavaScript Object (o)
///////////////////////////////////////////////////////////////////////////////
function ListToObject(a, l, f) {
var c = l.count;
for (var i = 0; i < c; i++) {
var t = l.getType(i);
switch (t) {
case DescValueType.BOOLEANTYPE:
a.push(l.getBoolean(i));
break;
case DescValueType.STRINGTYPE:
a.push(l.getString(i));
break;
case DescValueType.DOUBLETYPE:
a.push(l.getDouble(i));
break;
case DescValueType.INTEGERTYPE:
                a.push(l.getInteger(i));
                break;
            case DescValueType.LARGEINTEGERTYPE:
            a.push(l.getLargeInteger(i));
            break;
case DescValueType.OBJECTTYPE:
                var newT = l.getObjectType(i);
                var newV = l.getObjectValue(i);
                var newO = new Object();
                a.push(newO);
                DescriptorToObject(newO, newV, f);
                break;
case DescValueType.UNITDOUBLE:
                var newT = l.getUnitDoubleType(i);
                var newV = l.getUnitDoubleValue(i);
                var newO = new Object();
                a.push(newO);
                newO.type = typeIDToCharID(newT);
                newO.typeString = typeIDToStringID(newT);
                newO.value = newV;
                break;
case DescValueType.ENUMERATEDTYPE:
                var newT = l.getEnumerationType(i);
                var newV = l.getEnumerationValue(i);
                var newO = new Object();
                a.push(newO);
                newO.type = typeIDToCharID(newT);
                newO.typeString = typeIDToStringID(newT);
                newO.value = typeIDToCharID(newV);
                newO.valueString = typeIDToStringID(newV);
                break;
case DescValueType.CLASSTYPE:
                a.push(l.getClass(i));
                break;
case DescValueType.ALIASTYPE:
                a.push(l.getPath(i));
                break;
case DescValueType.RAWTYPE:
                var tempStr = l.getData(i);
                tempArray = new Array();
                for (var tempi = 0; tempi < tempStr.length; tempi++) { 
                    tempArray[tempi] = tempStr.charCodeAt(tempi); 
                }
                a.push(tempArray);
                break;
case DescValueType.REFERENCETYPE:
                var ref = l.getReference(i);
                var newO = new Object();
                a.push(newO);
                ReferenceToObject(newO, ref, f);
                break;
case DescValueType.LISTTYPE:
                var list = l.getList(i);
                var newO = new Object();
                a.push(newO);
                ListToObject(newO, list, f);
                break;
default:
myLogging.LogIt("Unsupported type in descriptorToObject " + t);
}
}
if (undefined != f) {
o = f(o);
}
}


///////////////////////////////////////////////////////////////////////////////
// Function: ReferenceToObject
// Usage: update a JavaScript Object from an ActionReference
// Input: JavaScript Object (o), current object to update (output)
//  Photoshop ActionReference (r), reference to pull new params for object from
//  JavaScript Function (f), post process converter utility to convert
// Return: Nothing, update is applied to passed in JavaScript Object (o)
///////////////////////////////////////////////////////////////////////////////
function ReferenceToObject(o, r, f) {
 // TODO : seems like I should output the desiredClass information here
 // maybe all references have a name, index, etc. and then the are either undefined
 // what about clobber, can I get an index in an index and then that would not work
 // should the second loop be doing something different?
    var originalRef = r;
while (r != null) {
var refForm = r.getForm();
        var refClass = r.getDesiredClass();
        var strk = app.typeIDToStringID(refClass);
        if (strk.length == 0) {
            strk = app.typeIDToCharID(refClass);
        }
switch (refForm) {
case ReferenceFormType.NAME:
o["name"] = r.getName();
break;
case ReferenceFormType.INDEX:
o["index"] = r.getIndex();
break;
case ReferenceFormType.IDENTIFIER:
                o["indentifier"] = r.getIdentifier();
                break;
case ReferenceFormType.OFFSET:
                o["offset"] = r.getOffset();
                break;
case ReferenceFormType.ENUMERATED:
                var newT = r.getEnumeratedType();
                var newV = r.getEnumeratedValue();
                o["enumerated"] = new Object();
                o["enumerated"].type = typeIDToCharID(newT);
                o["enumerated"].typeString = typeIDToStringID(newT);
                o["enumerated"].value = typeIDToCharID(newV);
                o["enumerated"].valueString = typeIDToStringID(newV);
                break;
case ReferenceFormType.PROPERTY:
                o["property"] = app.typeIDToStringID(r.getProperty());
                if (o["property"].length == 0) {
                    o["property"] = app.typeIDToCharID(r.getProperty());
                }
                break;
case ReferenceFormType.CLASSTYPE:
                o["class"] = refClass; // i already got that r.getDesiredClass(k);
                break;
default:
myLogging.LogIt("Unsupported type in referenceToObject " + t);
}
        r = r.getContainer();
        try {
            r.getDesiredClass();
        } catch(e) {
            r = null;
        }
}
if (undefined != f) {
o = f(o);
}
}



///////////////////////////////////////////////////////////////////////////////
// Library for logging, alert, ESTK console, or log file in JavaScript
///////////////////////////////////////////////////////////////////////////////
function MyLogging(inUseAlert, inUseWriteln, inUseLogFile, inLogFileString) {
    // member properties
    this.useAlert = undefined != inUseAlert ? inUseAlert : true;
    this.useWriteln = undefined != inUseWriteln ? inUseWriteln : true;
    this.useLogFile = undefined != inUseLogFile ? inUseLogFile : true;
    this.logFileString = undefined != inLogFileString ? inLogFileString : Folder.desktop + "/MyLogging.txt";
    // member methods
    this.LogIt = function(inString) {
        if (this.useAlert)
            alert(inString);
        if (this.useWriteln)
            $.writeln(inString);
        if (this.useLogFile) {
            var f = new File(this.logFileString);
            if (f.open('a')) {
                f.writeln(inString);
                f.close();
            }
        }
    }
    this.execute = function() {
        File( this.logFileString ).execute();
    }
}



///////////////////////////////////////////////////////////////////////////////
// Library for timing things in JavaScript
///////////////////////////////////////////////////////////////////////////////
function Timer() {
// member properties
this.startTime = new Date();
this.endTime = new Date();

// member methods

// reset the start time to now
this.start = function () { this.startTime = new Date(); }

// reset the end time to now
this.stop = function () { this.endTime = new Date(); }

// get the difference in milliseconds between start and stop
this.getTime = function () { return (this.endTime.getTime() - this.startTime.getTime()) / 1000; }

// get the current elapsed time from start to now, this sets the endTime
this.getElapsed = function () { this.endTime = new Date(); return this.getTime(); }
}
Photo of Tom Ruark

Tom Ruark, Official Rep

  • 26 Posts
  • 11 Reply Likes
And some commentary and results from my testing with mac CC 2015.1.2

// converting to a smart object, document has no layer compsGetSmartObjectInfo: ({
placed:{
   type:"", 
   typeString:"placed", 
   value:"", 
   valueString:"rasterizeContent"}, 
documentID:"", 
compsList:{
    compID:-1, 
    originalCompID:-1}, 
linked:false, 
fileReference:"Layer 1.psb"})

// document as three layers and three layer comps, each comp shows one layer, I picked "Comp A" 
GetSmartObjectInfo: ({
placed:{type:"", 
        typeString:"placed", 
        value:"", 
        valueString:"rasterizeContent"}, 
documentID:"xmp.did:919f50fe-8e48-465d-b6f2-a5aceb6cfa2b", 
compsList:{compID:-1, 
           originalCompID:467622325,  << this number matches the ID below for the one i picked
           compList:[({name:"Comp C", 
                       ID:447706030, 
                       comment:""}), 
                     ({name:"Comp B", 
                       ID:459000334, 
                       comment:""}), 
                     ({name:"Comp A", 
                       ID:467622325, 
                       comment:""})]}, 
            linked:false, 
            fileReference:"Layer 1.psb"})

// Now I am going to safe the original document and close it and the smart as well
// open it back up, open up the smart object and switch to Comp C
// and see what I get
GetSmartObjectInfo: ({
placed:{type:"", 
        typeString:"placed", 
        value:"", 
        valueString:"rasterizeContent"}, 
documentID:"xmp.did:919f50fe-8e48-465d-b6f2-a5aceb6cfa2b", 
compsList:{compID:-1, 
           originalCompID:447706030, << looks good this number matches the Comp C below
           compList:[({name:"Comp C", ID:447706030, comment:""}), 
                     ({name:"Comp B", ID:459000334, comment:""}), 
                     ({name:"Comp A", ID:467622325, comment:""})]}, 
            linked:false, 
            fileReference:"Layer 1.psb"})
            
// now I want a linked file so I can modify it
// Same as above, three layers "1" "2" "3" with comps with the same name, Comp 1 selected
GetSmartObjectInfo: ({placed:{type:"", typeString:"placed", value:"", valueString:"rasterizeContent"}, 
documentID:"xmp.did:635ddc08-1d6a-4fce-b53d-742132ba3620", 
compsList:{compID:-1, 
           originalCompID:960470793, << looks good this matches "Layer 1" id below
           compList:[({name:"Layer 1", ID:960470793, comment:""}), 
                     ({name:"Layer 2", ID:966101138, comment:""}), 
                     ({name:"Layer 3", ID:971664255, comment:""})]}, 
           linked:true, 
           fileReference:"LinkedFile.psd", 
           link:new File ("~/Desktop/LinkedFile.psd"), 
           linkMissing:false, 
           linkChanged:false})

// now i'm saving and closing all
// opening up LinkedFile.psd and switching to Layer 2 and saving
// then opening up original document and see what we get
GetSmartObjectInfo: ({placed:{type:"", typeString:"placed", value:"", valueString:"rasterizeContent"}, 
documentID:"xmp.did:635ddc08-1d6a-4fce-b53d-742132ba3620", 
compsList:{compID:-1, 
           originalCompID:960470793,  << still linking to Layer 1 which is incorrect and it tells me that below with linkChanged == true
           compList:[({name:"Layer 1", ID:960470793, comment:""}), 
                     ({name:"Layer 2", ID:966101138, comment:""}), 
                     ({name:"Layer 3", ID:971664255, comment:""})]}, 
           linked:true, fileReference:"LinkedFile.psd", 
           link:new File ("~/Desktop/LinkedFile.psd"), 
           linkMissing:false, 
           linkChanged:true})

// now open the linked document
// nothing happened until I picked "Update modified content"
GetSmartObjectInfo: ({placed:{type:"", typeString:"placed", value:"", valueString:"rasterizeContent"}, 
documentID:"xmp.did:635ddc08-1d6a-4fce-b53d-742132ba3620", 
compsList:{compID:-1, 
originalCompID:966101138, << now this looks better I've got a match and linkedChanged is back to false
compList:[({name:"Layer 1", ID:960470793, comment:""}), 
          ({name:"Layer 2", ID:966101138, comment:""}), 
          ({name:"Layer 3", ID:971664255, comment:""})]}, 
linked:true, 
fileReference:"LinkedFile.psd", 
link:new File ("~/Desktop/LinkedFile.psd"), 
linkMissing:false, 
linkChanged:false})

// I haven't been able to figure out a way to change the compID value
Photo of christoph pfaffenbichler

christoph pfaffenbichler, Champion

  • 1208 Posts
  • 166 Reply Likes
Thank you for your effort but, sorry, I don’t get it. 
What good is the SO-instance’s compID if it does not reflect the Layer Comp applied to the SO-instance (which is what I want to find out in the containing document)? 
And even with your code it seems stuck on -1, which should indicate that no Layer Comp is applied. 
Photo of Tom Ruark

Tom Ruark, Official Rep

  • 26 Posts
  • 11 Reply Likes
It appears to me that the originalCompID and linkChanged is giving you the information you want and you should not use compID.
Photo of christoph pfaffenbichler

christoph pfaffenbichler, Champion

  • 1208 Posts
  • 166 Reply Likes
I may have expressed myself unclearly, I want to know which Layer Comp is applied to a Smart Object instance and that information seems to be hidden even though compID should represent it. 
If I have applied three different Layer Comps to three SO instances and they all display the same compID then what does compID represent at all? 
Photo of Jaroslav Bereza

Jaroslav Bereza

  • 785 Posts
  • 169 Reply Likes
I wrote script which can get used compID on SO. It is not effective but it works (in most cases)!

1) get document histogram
2) get list IDs of comps inside SO as array
3) change SO comp
4) get new histogram and compare with old
4a)if is new is same as old you have compID
4b) if not - repeat until histograms are same

There is small possibility to have wrong result. For better accuracy you can compare layer bounds and histogram of all channels separately. You also can check all comps and throw error on two and more accordance.

Script here: http://pastebin.com/dhBZwC4m (sorry for "JAM framework" balast :-) )
Photo of christoph pfaffenbichler

christoph pfaffenbichler, Champion

  • 1228 Posts
  • 173 Reply Likes
Sorry, haven’t been able to check it out yet but thanks! 
Photo of christoph pfaffenbichler

christoph pfaffenbichler, Champion

  • 1228 Posts
  • 173 Reply Likes
Finally checked it out. 
Nicely done, but as you mentioned false positives are a possibility (simplest case two LayerComps that are a half-black/half-white image and a half-white/half-black image). 
Photo of Jaroslav Bereza

Jaroslav Bereza

  • 785 Posts
  • 169 Reply Likes
Another problem is, if you have this layer completly under anothers so histogram is always same. Or if layer is somehow invisible. Another possibility could be use ColorPicker, it has option pick only current layer. But I don't know fast could it be and how pick color on specific position. Script listener doesn't record position and I can't use "Allow tool recording" in action panel... it's disabled somehow... maybe I can't turn this on if script listener is enabled.

Histogram is also very slow, so I check layer bounds and coordinates first (it's dependent on transparent pixels by default) and if there are two or more possitive results, then I check only histograms for comps with same results.
Photo of Jaroslav Bereza

Jaroslav Bereza

  • 785 Posts
  • 169 Reply Likes
In worst case you can read PSD file :-D 

Photo of christoph pfaffenbichler

christoph pfaffenbichler, Champion

  • 1228 Posts
  • 173 Reply Likes
Apart from the fact I would not know how to do that (and I’m probably not the only »light« Photoshop Scripter) and that the file would have to be saved first to make sure possible recent changes are reflected in the file’s data ... what good was introducing the Smart Object’s compID then? 
Photo of Jaroslav Bereza

Jaroslav Bereza

  • 785 Posts
  • 169 Reply Likes
Here is PSD file specification: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/

Yes it must be saved. Saving as copy in win temp dir could be ok. "As copy" option doesn't change file path if you hit CTRL + S.
You can download trial Neo Hex editor and use structure viewer to make better idea how structure works. There is also PSD parser script written in "C".  

Parser in JS could look like this: http://pastebin.com/tS7YDeRf (this is experiment, can contains bugs) LayerID is somewhere in LayerData[0]-LayerData[n] section .... But if you will search "complong" substring in this range, you will found it.
Photo of Max Johnson

Max Johnson, Champion

  • 457 Posts
  • 212 Reply Likes
Still an issue in CC2017 update
Photo of Jaroslav Bereza

Jaroslav Bereza

  • 785 Posts
  • 169 Reply Likes
Yesterday I watched and tried new features in Affinity Designer for Windows and I must say that Photoshop definitely will no longer application used by profesionals UI designers. (except skeuomorphistic UI) I'm not sure if I prefer Affinity or Experience designer. I still need wait a little bit for all features I need. Affinity has big advantage, but XD has another interesting features and some promising features on the roadmap.

However, I think next year about this time I could migrate everything from Photoshop to another application.
Photo of Max Johnson

Max Johnson, Champion

  • 457 Posts
  • 212 Reply Likes
I am really hoping we can get this all sorted out in the next release! I'm assuming there will be an interstitial bugfix release coming down the line, so could Adobe please try to fix the issue with compID always returning -1?

I am excited that it looks like you can get a complist with comp IDs for all comps in a smart object. Really great work! Very useful. : "OK, Smart Object descriptor info does include the ID of the currently selected comp for the child document, which will be -1 if none are selected.

The list of comps known is not exposed anywhere but in the PSD file. I'll see if I can add the list of comps (and other comp data) to the Smart Object Info for the next release. It'll be read only -- the only data you can change is the current comp ID value."

Thanks Chris!

Next if we can just have the compID of the smart object reflect the correct comp ID that is currently applied to the smart object, I would have all the tools I need to make super amazing smart object layer comp management panels for my co-workers!
Photo of Jaroslav Bereza

Jaroslav Bereza

  • 785 Posts
  • 169 Reply Likes
Looks like that in PS CC 2017 is more info about SmartObjects. 

layer{
   smartObjectMore{
     comp: yourID
   } 
}



http://sklad.bereza.cz/00-jarda/00_screenshot/2017-03-18_234924.jpg
Photo of Jaroslav Bereza

Jaroslav Bereza

  • 785 Posts
  • 169 Reply Likes
Solved and answered. You can close this topic.

In CC2017 I can get this info :-)
Photo of christoph pfaffenbichler

christoph pfaffenbichler, Champion

  • 1228 Posts
  • 173 Reply Likes
Been a while but if I remember correctly getting the list was not the problem, determining the applied Layer Comp was. 
And that still seems faulty to me using plain AM-code (returning -1 for all cases). 
Could you please share your code? 
Photo of Max Johnson

Max Johnson, Champion

  • 457 Posts
  • 212 Reply Likes
I can't seem to find the thread, but there was another post where someone discovered the new (as of CC 2017) AM property on smart objects called 'smartObjectMore'.

The 'comp' property in smartObjectMore is the correct applied compID. The 'compID' in the 'compsList' where we were looking in previous versions is still broken and misleading.
Photo of Jaroslav Bereza

Jaroslav Bereza

  • 785 Posts
  • 169 Reply Likes
Max has right.
Photo of christoph pfaffenbichler

christoph pfaffenbichler, Champion

  • 1228 Posts
  • 173 Reply Likes
Thanks, Max, for the explanation! 
And thanks, Jaroslav, for following up on the issue in the first place, I for one might have missed the solution to this one otherwise! 

And great to see Scripting considerations are getting attention at the Photoshop team. 
Photo of aflexus

aflexus

  • 1 Post
  • 0 Reply Likes
CC 2018. "comp" property still equal -1 for all the objects...
Photo of christoph pfaffenbichler

christoph pfaffenbichler, Champion

  • 1228 Posts
  • 173 Reply Likes
As per Max Johnson and Jaroslav Bereza’s posts over at 
https://feedback.photoshop.com/photoshop_family/topics/ps-script-how-to-get-list-of-layer-comps-in-s...
the issue is resolved. 

var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
var layerDesc = executeActionGet(ref);
var theSOMore = layerDesc.getObjectValue(stringIDToTypeID("smartObjectMore"));
alert (theSOMore.getInteger(stringIDToTypeID("comp")));
Photo of Fred Jones

Fred Jones

  • 1 Post
  • 0 Reply Likes
That gives the ID of the currently selected layer comp, but how do you get a list of all available comps? 
Photo of christoph pfaffenbichler

christoph pfaffenbichler, Champion

  • 1221 Posts
  • 170 Reply Likes
// 2018, use it at your own risk;
#target photoshop
if (app.documents.length > 0) {
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
var layerDesc = executeActionGet(ref);
if (layerDesc.hasKey(stringIDToTypeID("smartObject")) == true) {
var theSO = layerDesc.getObjectValue(stringIDToTypeID("smartObject"));
var x = theSO.getList(stringIDToTypeID("compsList"));
var theCompsList = theSO.getObjectValue(stringIDToTypeID("compsList"));
if (theCompsList.count > 2) {
var theCompsList = theCompsList.getList(stringIDToTypeID("compList"));
var theSOComps = new Array;
for (var m = 0; m < theCompsList.count; m++) {
var thisOne = theCompsList.getObjectValue(m);
var theName = thisOne.getString(stringIDToTypeID("name"));
var theID = thisOne.getInteger(stringIDToTypeID("ID"));
var theComment = thisOne.getString(stringIDToTypeID("comment"));
theSOComps.push([theName, theID, theComment])
};
var theSOMore = layerDesc.getObjectValue(stringIDToTypeID("smartObjectMore"));
var appliedComp = theSOMore.getInteger(stringIDToTypeID("comp"));
alert ("the comps\n"+theSOComps.join("\n"));
alert (appliedComp);
};
};
};
Photo of Fred Jones

Fred Jones

  • 1 Post
  • 0 Reply Likes
Works like a charm!  Thanks for the help.  Much appreciated!!