Skip to main content
RoyBoySolorio
Inspiring
September 14, 2022
Answered

Script or action to sync Layer comps outside Smart Object to Layer comp inside smart object

  • September 14, 2022
  • 4 replies
  • 2670 views

Hi all,

I need just one last bit to complete an epic process. I have a multi language Smart Object embeded in a working psd.  In the Smart Object is a Layer Comp that controls visibility of each language.  Outside the Smart Object is the same Layer Comp list.  Using the properties panel I can sync the two Layer Comps, controlling the language visibility.  But it's a manual process.

When trying to build that behavior as an action using the "Next" button in the Layer Comp panel and the same named Layer Comp in the properties panel, I get an ID number.  Which would be fine, except when tried on a new working psd. Then it's not recognized.

 

If it's helpful, both in and out Layer Comps have identical names i.e. EN, JA, SP, FR, DA, ZH in the same order starting with EN.  Thanks in advance for any guidance.

 

This topic has been closed for replies.
Correct answer c.pfaffenbichler

I guess »Layer Comp Selection for Smart Objects« would have to be set in the Layer Comps in the containing document, but otherwise this might work: 

// if smart object has layer comps with same names as containing document select them and ipdate containing doc’s comps;
// 2022, use it at your own risk;
if (app.documents.length > 0) {
var myDocument = activeDocument;
var theLayerComps = getLayerCompsArray ();
var theSmartObjects = collectSmartObjectsLayerComps ();
// process layer comps;
for (var y = 0; y < theLayerComps.length; y++) {
    var thisComp = theLayerComps[y];
    for (var z = 0; z < theSmartObjects.length; z++) {
        for (var x = 0; x < theSmartObjects[z][2].length; x++) {
            var thisSOLC = theSmartObjects[z][2][x];
            if (thisComp[0] == thisSOLC[0]) {
                applyLayerComp (thisComp[2]);
                applyLayerCompToSmartObject(theSmartObjects[z][1], thisSOLC[1]);
            }
        }
        updateLayerComp ();
    };
}
};
////////////////////////////////////#
////// collect smart objects, probably based on code by paul, mike or x //////
function collectSmartObjectsLayerComps () {
// get number of layers;
    var ref = new ActionReference();
    ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
    var applicationDesc = executeActionGet(ref);
    var theNumber = applicationDesc.getInteger(stringIDToTypeID("numberOfLayers"));
// process the layers;
    var theLayers = new Array;
    for (var m = 0; m <= theNumber; m++) {
    try {
    var ref = new ActionReference();
    ref.putIndex( charIDToTypeID( "Lyr " ), m);
    var layerDesc = executeActionGet(ref);
    var layerSet = typeIDToStringID(layerDesc.getEnumerationValue(stringIDToTypeID("layerSection")));
    var isBackground = layerDesc.getBoolean(stringIDToTypeID("background"));
// if not layer group collect values;
    if (layerSet != "layerSectionEnd" && layerSet != "layerSectionStart" && isBackground != true) {
    var theName = layerDesc.getString(stringIDToTypeID('name'));
    var theID = layerDesc.getInteger(stringIDToTypeID('layerID'));
// is smart object;
    if(layerDesc.hasKey(stringIDToTypeID('smartObject'))) {
// comps;
        var theSO = layerDesc.getObjectValue(stringIDToTypeID("smartObject"));
        var theCompsList = theSO.getObjectValue(stringIDToTypeID("compsList"));
        if (theCompsList.count > 2) {
        var theCompsList = theCompsList.getList(stringIDToTypeID("compList"));
        var theSOComps = new Array;
        for (var n = 0; n < theCompsList.count; n++) {
        var thisOne = theCompsList.getObjectValue(n);
        var theCompName = thisOne.getString(stringIDToTypeID("name"));
        var theCompID = thisOne.getInteger(stringIDToTypeID("ID"));
        var theComment = thisOne.getString(stringIDToTypeID("comment"));
        theSOComps.push([theCompName, theCompID, theComment]);
        };
// add to array;
        theLayers.push([theName, theID, theSOComps])
    }
}
}
} catch (e) {};
};
return theLayers
};
////// get array of layer comps’ properties //////
function getLayerCompsArray () {
    var ref = new ActionReference();
    ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
    var docDesc = executeActionGet(ref);
    var compList = docDesc.getList(stringIDToTypeID("compsList"));
    var theComps = new Array;
    for (var c = 0; c < compList.count; c++) {
    var thisOne = compList.getObjectValue(c);
    theComps.push(getObjectDesc(thisOne))
    };
    return theComps;
    };
////// based on code by michael l hale //////
function getObjectDesc (theDesc) {
    var c = theDesc.count;
    var str = new Array;
    for(var i=0;i<c;i++){ //enumerate descriptor's keys
    //	str.push([i+' = '+typeIDToStringID(theDesc.getKey(i))+': '+theDesc.getType(theDesc.getKey(i))+'\n'+getValues (theDesc, i)]);
        str.push(getValues (theDesc, i));
        };
    return(str);
    };
////// check //////
function getValues (theDesc, theNumber) {
    switch (theDesc.getType(theDesc.getKey(theNumber))) {
    case DescValueType.ALIASTYPE:
    return theDesc.getPath(theDesc.getKey(theNumber));
    break;
    case DescValueType.BOOLEANTYPE:
    return theDesc.getBoolean(theDesc.getKey(theNumber));
    break;
    case DescValueType.CLASSTYPE:
    return theDesc.getClass(theDesc.getKey(theNumber));
    break;
    case DescValueType.DOUBLETYPE:
    return theDesc.getDouble(theDesc.getKey(theNumber));
    break;
    case DescValueType.ENUMERATEDTYPE:
    return (typeIDToStringID(theDesc.getEnumerationValue(theDesc.getKey(theNumber)))+"_"+typeIDToStringID(theDesc.getEnumerationType(theDesc.getKey(theNumber))));
    break;
    case DescValueType.INTEGERTYPE:
    return theDesc.getInteger(theDesc.getKey(theNumber));
    break;
    case DescValueType.LISTTYPE:
    return theDesc.getList(theDesc.getKey(theNumber));
    break;
    case DescValueType.OBJECTTYPE:
    return (theDesc.getObjectValue(theDesc.getKey(theNumber))+"_"+typeIDToStringID(theDesc.getObjectType(theDesc.getKey(theNumber))));
    break;
    case DescValueType.RAWTYPE:
    return theDesc.getReference(theDesc.getData(theNumber));
    break;
    case DescValueType.REFERENCETYPE:
    return theDesc.getReference(theDesc.getKey(theNumber));
    break;
    case DescValueType.STRINGTYPE:
    return theDesc.getString(theDesc.getKey(theNumber));
    break;
    case DescValueType.UNITDOUBLE:
    return (theDesc.getUnitDoubleValue(theDesc.getKey(theNumber))+"_"+typeIDToStringID(theDesc.getUnitDoubleType(theDesc.getKey(theNumber))));
    break;
    default: 
    break;
    };
    };
////// apply layercomp to smart object //////
function applyLayerCompToSmartObject (theLayerID, theID) {
    selectLayerByID(theLayerID, false);
        var desc9 = new ActionDescriptor();
            var ref3 = new ActionReference();
//            ref3.putIdentifier(charIDToTypeID( "Lyr " ), theLayerID);
            ref3.putEnumerated(charIDToTypeID( "Lyr " ), charIDToTypeID( "Ordn" ), charIDToTypeID( "Trgt" ));
        desc9.putReference( charIDToTypeID( "null" ), ref3 );
        desc9.putInteger( stringIDToTypeID( "compID" ), theID );
    executeAction( stringIDToTypeID( "setPlacedLayerComp" ), desc9, DialogModes.NO );
    };
////// apply layer comp //////
function applyLayerComp (theID) {
    // =======================================================
    var desc6 = new ActionDescriptor();
    var ref3 = new ActionReference();
    ref3.putIdentifier( stringIDToTypeID( "compsClass" ), theID );
    //ref3.putName( stringIDToTypeID( "compsClass" ), theName );
    desc6.putReference( stringIDToTypeID( "null" ), ref3 );
    executeAction( stringIDToTypeID( "applyComp" ), desc6, DialogModes.NO );
    };
////// update layer comp //////
function updateLayerComp () {
        var desc13 = new ActionDescriptor();
            var ref8 = new ActionReference();
            ref8.putEnumerated( stringIDToTypeID( "compsClass" ), stringIDToTypeID( "ordinal" ), stringIDToTypeID( "targetEnum" ) );
        desc13.putReference( stringIDToTypeID( "null" ), ref8 );
    executeAction( stringIDToTypeID( "recapture" ), desc13, DialogModes.NO );
    };
////// based on code by mike hale and paul riggott //////
function selectLayerByID(index,add){ 
    add = undefined ? add = false:add 
    var ref = new ActionReference();
        ref.putIdentifier(charIDToTypeID("Lyr "), index);
        var desc = new ActionDescriptor();
        desc.putReference(charIDToTypeID("null"), ref );
            if(add) desc.putEnumerated( stringIDToTypeID( "selectionModifier" ), stringIDToTypeID( "selectionModifierType" ), stringIDToTypeID( "addToSelection" ) ); 
            desc.putBoolean( charIDToTypeID( "MkVs" ), false ); 
        try{
        executeAction(charIDToTypeID("slct"), desc, DialogModes.NO );
    }catch(e){
    alert(e.message); 
    }
    };

4 replies

RoyBoySolorio
Inspiring
September 28, 2022

Ok, working comp posted. Someone asked about the whole process, so with a hat tip to c.pfaffenbichler I'll go through the workflow.

Part 1, building the UI to be used as a Smart Object for various product angles.   Using pngs, exported from Figma, with as many as 20 Locales.  The first action calls up the "Load Files into Stack Script". In the Posted PSD, the images brought in are EN, JA, NO, IT.  The action continues by building out a group for each, based on the language and putting a vector hiding mask over each group. The action continues by sorting the correct image layer into their named group. Finally the action makes a name based Layer Comp, controlling the visibility of each group. This saves as a UI Build that will go into various assets. In the end this psd has four Layer Comps named EN, JA, NO and IT

 

Part 2 is to place the UI Build as a Smart Object into the product or lifestyle comp.  Once complete an action builds out the same layer Comps EN, JA, NO, and IT.  Now both Layer Comps need to be synced/paired, the one inside the Smart Object with the Layer Comp in the working comp. This is the part c.pfaffenbichler resolved.  That action pairs and syncs them, doing away with a manual, potentially erroneous, result.  Now clicking on the Layer Comp in the worker, drives the change in the smart object with out having to open it.

 

The last action builds on the "Export Layer Comps to Files" script, with some twists, but in the end I'll have 4 files named EN.psd, JA.psd, NO.psd, and IT.psd

 

Hope this helps prompt new workflow ideas.

~Roy
PECourtejoie
Community Expert
October 2, 2022

Thank you very much for this explanation, this forum is more complete thanks to you!

c.pfaffenbichler
c.pfaffenbichlerCorrect answer
Community Expert
September 25, 2022

I guess »Layer Comp Selection for Smart Objects« would have to be set in the Layer Comps in the containing document, but otherwise this might work: 

// if smart object has layer comps with same names as containing document select them and ipdate containing doc’s comps;
// 2022, use it at your own risk;
if (app.documents.length > 0) {
var myDocument = activeDocument;
var theLayerComps = getLayerCompsArray ();
var theSmartObjects = collectSmartObjectsLayerComps ();
// process layer comps;
for (var y = 0; y < theLayerComps.length; y++) {
    var thisComp = theLayerComps[y];
    for (var z = 0; z < theSmartObjects.length; z++) {
        for (var x = 0; x < theSmartObjects[z][2].length; x++) {
            var thisSOLC = theSmartObjects[z][2][x];
            if (thisComp[0] == thisSOLC[0]) {
                applyLayerComp (thisComp[2]);
                applyLayerCompToSmartObject(theSmartObjects[z][1], thisSOLC[1]);
            }
        }
        updateLayerComp ();
    };
}
};
////////////////////////////////////#
////// collect smart objects, probably based on code by paul, mike or x //////
function collectSmartObjectsLayerComps () {
// get number of layers;
    var ref = new ActionReference();
    ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
    var applicationDesc = executeActionGet(ref);
    var theNumber = applicationDesc.getInteger(stringIDToTypeID("numberOfLayers"));
// process the layers;
    var theLayers = new Array;
    for (var m = 0; m <= theNumber; m++) {
    try {
    var ref = new ActionReference();
    ref.putIndex( charIDToTypeID( "Lyr " ), m);
    var layerDesc = executeActionGet(ref);
    var layerSet = typeIDToStringID(layerDesc.getEnumerationValue(stringIDToTypeID("layerSection")));
    var isBackground = layerDesc.getBoolean(stringIDToTypeID("background"));
// if not layer group collect values;
    if (layerSet != "layerSectionEnd" && layerSet != "layerSectionStart" && isBackground != true) {
    var theName = layerDesc.getString(stringIDToTypeID('name'));
    var theID = layerDesc.getInteger(stringIDToTypeID('layerID'));
// is smart object;
    if(layerDesc.hasKey(stringIDToTypeID('smartObject'))) {
// comps;
        var theSO = layerDesc.getObjectValue(stringIDToTypeID("smartObject"));
        var theCompsList = theSO.getObjectValue(stringIDToTypeID("compsList"));
        if (theCompsList.count > 2) {
        var theCompsList = theCompsList.getList(stringIDToTypeID("compList"));
        var theSOComps = new Array;
        for (var n = 0; n < theCompsList.count; n++) {
        var thisOne = theCompsList.getObjectValue(n);
        var theCompName = thisOne.getString(stringIDToTypeID("name"));
        var theCompID = thisOne.getInteger(stringIDToTypeID("ID"));
        var theComment = thisOne.getString(stringIDToTypeID("comment"));
        theSOComps.push([theCompName, theCompID, theComment]);
        };
// add to array;
        theLayers.push([theName, theID, theSOComps])
    }
}
}
} catch (e) {};
};
return theLayers
};
////// get array of layer comps’ properties //////
function getLayerCompsArray () {
    var ref = new ActionReference();
    ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
    var docDesc = executeActionGet(ref);
    var compList = docDesc.getList(stringIDToTypeID("compsList"));
    var theComps = new Array;
    for (var c = 0; c < compList.count; c++) {
    var thisOne = compList.getObjectValue(c);
    theComps.push(getObjectDesc(thisOne))
    };
    return theComps;
    };
////// based on code by michael l hale //////
function getObjectDesc (theDesc) {
    var c = theDesc.count;
    var str = new Array;
    for(var i=0;i<c;i++){ //enumerate descriptor's keys
    //	str.push([i+' = '+typeIDToStringID(theDesc.getKey(i))+': '+theDesc.getType(theDesc.getKey(i))+'\n'+getValues (theDesc, i)]);
        str.push(getValues (theDesc, i));
        };
    return(str);
    };
////// check //////
function getValues (theDesc, theNumber) {
    switch (theDesc.getType(theDesc.getKey(theNumber))) {
    case DescValueType.ALIASTYPE:
    return theDesc.getPath(theDesc.getKey(theNumber));
    break;
    case DescValueType.BOOLEANTYPE:
    return theDesc.getBoolean(theDesc.getKey(theNumber));
    break;
    case DescValueType.CLASSTYPE:
    return theDesc.getClass(theDesc.getKey(theNumber));
    break;
    case DescValueType.DOUBLETYPE:
    return theDesc.getDouble(theDesc.getKey(theNumber));
    break;
    case DescValueType.ENUMERATEDTYPE:
    return (typeIDToStringID(theDesc.getEnumerationValue(theDesc.getKey(theNumber)))+"_"+typeIDToStringID(theDesc.getEnumerationType(theDesc.getKey(theNumber))));
    break;
    case DescValueType.INTEGERTYPE:
    return theDesc.getInteger(theDesc.getKey(theNumber));
    break;
    case DescValueType.LISTTYPE:
    return theDesc.getList(theDesc.getKey(theNumber));
    break;
    case DescValueType.OBJECTTYPE:
    return (theDesc.getObjectValue(theDesc.getKey(theNumber))+"_"+typeIDToStringID(theDesc.getObjectType(theDesc.getKey(theNumber))));
    break;
    case DescValueType.RAWTYPE:
    return theDesc.getReference(theDesc.getData(theNumber));
    break;
    case DescValueType.REFERENCETYPE:
    return theDesc.getReference(theDesc.getKey(theNumber));
    break;
    case DescValueType.STRINGTYPE:
    return theDesc.getString(theDesc.getKey(theNumber));
    break;
    case DescValueType.UNITDOUBLE:
    return (theDesc.getUnitDoubleValue(theDesc.getKey(theNumber))+"_"+typeIDToStringID(theDesc.getUnitDoubleType(theDesc.getKey(theNumber))));
    break;
    default: 
    break;
    };
    };
////// apply layercomp to smart object //////
function applyLayerCompToSmartObject (theLayerID, theID) {
    selectLayerByID(theLayerID, false);
        var desc9 = new ActionDescriptor();
            var ref3 = new ActionReference();
//            ref3.putIdentifier(charIDToTypeID( "Lyr " ), theLayerID);
            ref3.putEnumerated(charIDToTypeID( "Lyr " ), charIDToTypeID( "Ordn" ), charIDToTypeID( "Trgt" ));
        desc9.putReference( charIDToTypeID( "null" ), ref3 );
        desc9.putInteger( stringIDToTypeID( "compID" ), theID );
    executeAction( stringIDToTypeID( "setPlacedLayerComp" ), desc9, DialogModes.NO );
    };
////// apply layer comp //////
function applyLayerComp (theID) {
    // =======================================================
    var desc6 = new ActionDescriptor();
    var ref3 = new ActionReference();
    ref3.putIdentifier( stringIDToTypeID( "compsClass" ), theID );
    //ref3.putName( stringIDToTypeID( "compsClass" ), theName );
    desc6.putReference( stringIDToTypeID( "null" ), ref3 );
    executeAction( stringIDToTypeID( "applyComp" ), desc6, DialogModes.NO );
    };
////// update layer comp //////
function updateLayerComp () {
        var desc13 = new ActionDescriptor();
            var ref8 = new ActionReference();
            ref8.putEnumerated( stringIDToTypeID( "compsClass" ), stringIDToTypeID( "ordinal" ), stringIDToTypeID( "targetEnum" ) );
        desc13.putReference( stringIDToTypeID( "null" ), ref8 );
    executeAction( stringIDToTypeID( "recapture" ), desc13, DialogModes.NO );
    };
////// based on code by mike hale and paul riggott //////
function selectLayerByID(index,add){ 
    add = undefined ? add = false:add 
    var ref = new ActionReference();
        ref.putIdentifier(charIDToTypeID("Lyr "), index);
        var desc = new ActionDescriptor();
        desc.putReference(charIDToTypeID("null"), ref );
            if(add) desc.putEnumerated( stringIDToTypeID( "selectionModifier" ), stringIDToTypeID( "selectionModifierType" ), stringIDToTypeID( "addToSelection" ) ); 
            desc.putBoolean( charIDToTypeID( "MkVs" ), false ); 
        try{
        executeAction(charIDToTypeID("slct"), desc, DialogModes.NO );
    }catch(e){
    alert(e.message); 
    }
    };
RoyBoySolorio
Inspiring
September 25, 2022

Super excited to test this out. This last piece of the puzzle is the only
part that is done manually. I'll be checking it out Monday night.

I guess part of being so busy is because you're good at what you do. May I
ask, why do you help folks like me out, probably on your own time?
Regardless of the outcome here, I am extremely grateful for your gift.
Thank you.

Regards,

(personal info removed)

~Roy
RoyBoySolorio
Inspiring
September 27, 2022

ABSOLUTLY BRILLIANT.  Did exactly what I needed. Thank you, thank you.

~Roy
Bojan Živković11378569
Community Expert
September 15, 2022

I downloaded and tested using your file. If you always have same layer comps, actually layer comps with same name in embedded file and you need exactly same layer comps in working document then the solution can be following:

 

  • Select embedded smart object manually or using action if its always named with exactly same name or it is always in the same relative position.
  • Edit contents,
  • Apply layer comp "EN"
  • Close with saving
  • Create new layer comp named EN
  • Edit contents
  • Apply layer comp "JA"
  • Close with saving
  • Create new layer comp named "JA"
  • Edit contents...

 

Here is how it looks when recorded without ID but with hard coded layer comp names.

 

In case that you have layer comps with different names in each new file and you want to copy name and create new layer comp with the same name in the working document then I must inform you it is not possible using Photoshop action.

RoyBoySolorio
Inspiring
September 15, 2022

Thanks Bojan,  I appreciate it but with the export layer comp system already established, entering an exiting the smart object doesn't flow with that.  I keep looking around.

~Roy
Bojan Živković11378569
Community Expert
September 15, 2022

I do not understand what exactly you mean but let me try one more time. You have problem because there are already created layer comps in working file? Delete them , simply delete them for what you can record action steps. If you are always using same working file run same action with steps to delete existing layer comps then to open smart object and do things as shown above in screenshot with expanded action steps.

Bojan Živković11378569
Community Expert
September 14, 2022

I do not understand what exactly is problem. Testing Apply next layer comp using action and it works just fine. Can you post some screenshot to clarify what exactly you are doing and what is the problem?

 

This sentence is esspecially confusing to me: "When trying to build that behavior as an action using the "Next" button in the Layer Comp panel and the same named Layer Comp in the properties panel, I get an ID number. "

 

Here is my take, assuming that you have PSD file with layer comps which is placed inside another file. Action looks like this:
Edit Contents (embedded smart object with layer comps must be selected, record step before for that if necessary)

Apply next layer comp

Close (Saving:yes)

 

RoyBoySolorio
Inspiring
September 14, 2022

My appologies, this is a complex workflow and I'm not sure a screen cap will do it. All this is for use with Export  Layer Comps, which creates specific psds named per the layer comp.  That part is fine.

 

There are two identical layer comp lists.  One inside the embedded Smart object, EN, JA, NO.  The other in the comp/working psd, EN, JA, NO.  Again an Inside Layer Comp and Outside Layer Comp. To link the two I select "EN" Outside LC and via the Properties Panel go to the pull down menu and select the "EN" in the embedded LC, then sync/update the Outside layer comp.  I can manually do that for all the flags. When done, selecting the "NO" layer comp, changes the embedded Smart Object to the "NO" flag, and so on.

 

Making an action to select the next Outside LC and selecting the corresponding embedded LC via the properties panel comes up with a ID number unique to that choice.  The action fails on the next PSD. 

~Roy
RoyBoySolorio
Inspiring
September 14, 2022

My example is dumbed down and privacy concerns keep me from showing anything accurate  The end ask is for hundreds of combinations between product and language localization.


And yes the export layer comp script does exactly that.  It travels down the list of layer comps, adjusts the embedded layer comp as synced, then exports a separate file for each.

~Roy