14 Replies Latest reply on Oct 25, 2017 12:25 PM by Max Johnson - PlayStudios

    File information for smart objects

    JSinha

      I need to get some information for the smart objects in PSD file using PS scripting.

      1. I need to get the file path for the smart objects in a PSD. I found that for linked smart objects, the file path is accessible by opening it up as a document. But it would be nice if I could get the path without opening it up. Is there a way to achieve this using script? Also, for embedded smart objects is it possible to get the file path?

      2. Is there a way to distinguish between linked smart objects and embedded smart object using script?

       

      Thanks much in advance for any help.

        • 1. Re: File information for smart objects
          c.pfaffenbichler Level 9
          Also, for embedded smart objects is it possible to get the file path?

          What file path?

          Embedded SOs are just that, embedded in the containing document and are written to disk only when opened as temp files.

          • 2. Re: File information for smart objects
            JSinha Level 1

            I was wondering if we have any reference to the file from which the embedded SO was created, which prompted my question. I get it now that it is no longer available once the file is embedded. Thanks for the reply.

            • 3. Re: File information for smart objects
              c.pfaffenbichler Level 9

              I’ve used this to try and find a distinction between embedded and linked SO, but so far no luck.

              #target photoshop

              if (app.documents.length > 0) {

              var ref = new ActionReference();

              ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );

              var layerDesc = executeActionGet(ref);

              checkDesc2(layerDesc);

              var soDesc = layerDesc.getObjectValue(stringIDToTypeID('smartObject'));

              var placedDesc = soDesc.getEnumerationValue(stringIDToTypeID('placed'));

              checkDesc2 (soDesc);

              };

              //////

              ////// based on code by michael l hale //////

              function checkDesc2 (theDesc) {

              var c = theDesc.count;

              var str = '';

              for(var i=0;i<c;i++){ //enumerate descriptor's keys

                str = str + 'Key '+i+' = '+typeIDToStringID(theDesc.getKey(i))+': '+theDesc.getType(theDesc.getKey(i))+'\n'+getValues (theDesc, i)+'\n';

                };

              alert("desc\n\n"+str);

              };

              ////// check //////

              function getValues (theDesc, theNumber) {

              switch (theDesc.getType(theDesc.getKey(theNumber))) {

              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)))+"_"+typeIDToStr ingID(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.getObject Type(theDesc.getKey(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.getUn itDoubleType(theDesc.getKey(theNumber))));

              break;

              default:

              break;

              };

              };

               

              Hopefully Mike Hale will drop by, he might have more to contribute.

              • 4. Re: File information for smart objects
                c.pfaffenbichler Level 9

                Seems the paths are stored in the document’s XMP Metadata.

                embeddedSmartObjectPath.jpg

                • 5. Re: File information for smart objects
                  JSinha Level 1

                  Thank you so much for your time and prompt responses.

                   

                  I noticed too that the paths are stored in the XMP metadata. But the problem I am facing is mapping these paths to the SOs in my layers.

                  E.g I have the below layers in my file:

                  Layer1 comprising LinkedSO1

                  Layer2 comprising LinkedSO2

                  Layer3 comprising EmbeddedSO1

                  Layer4 comprising LinkedSO3

                   

                  I noticed that In the XMP, the paths will appear in the order

                  LinkedSO3_Path

                  LinkedSO2_Path

                  LinkedSO1_Path

                   

                  Now for mapping these paths to the layers, my script has to be knowledgeable enough to skip Layer3, as it has an Embedded SO. That's where I am stuck with this approach as I am unable to distinguish between linked and embedded SOs .

                  • 6. Re: File information for smart objects
                    c.pfaffenbichler Level 9

                    Comparing the fileReference String with the paths in the metadata is the closest I noticed – but it would not work in all possible cases, as a linked and an embedded Smart Object may have the same original name  …

                     

                    But "Export Contents" is only possible with embedded SOs, "Embed Linked" only with linked SOs, so maybe a try-clause could be employed, something like this

                    // 2014, use it at your own risk;

                    #target "photoshop-70.032"

                    try {

                    var theDoc = app.activeDocument;

                    // =======================================================

                    var idplacedLayerConvertToEmbedded = stringIDToTypeID( "placedLayerConvertToEmbedded" );

                    executeAction( idplacedLayerConvertToEmbedded, undefined, DialogModes.NO );

                    theDoc.activeHistoryState = theDoc.historyStates[theDoc.historyStates.length-2];

                    alert ("linked")

                    }

                    catch (e) {alert ("embedded")};

                     

                    Edit: Of course this is a fairly clumsy work-around.

                    • 7. Re: File information for smart objects
                      JJMack Most Valuable Participant

                      Also remember some embedded object may have no association with any file till the current document is saved. Or a temp file for working on it is created by Photoshop.  For example you open a new document add some layer put the onto a group and convert the group to a smart object layer.  The new document has no backing file other the possibly a Photoshop auto-save recovery psb for the document has never been saved.  The layers now only reside in the embedded object.  They will they will be written to a file in your user id temp space if you double click in the layer smart icon on the layer palette by Photoshop and be opened in Photoshop as a temp work document so you can work on the smart object.

                      • 8. Re: File information for smart objects
                        c.pfaffenbichler Level 9

                        // 2014, use it at your own risk;

                        #target "photoshop-70.032"

                        alert (linkedOrEmbedded(app.activeDocument.activeLayer));

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

                        function linkedOrEmbedded (theLayer) {

                        var theDoc = app.activeDocument;

                        app.activeDocument.activeLayer = theLayer;

                        var ref = new ActionReference();

                        ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );

                        var layerDesc = executeActionGet(ref);

                        var isSmartObject = layerDesc.hasKey(stringIDToTypeID('smartObject'));

                        if (isSmartObject == true) {

                        try {

                        // =======================================================

                        var idplacedLayerConvertToEmbedded = stringIDToTypeID( "placedLayerConvertToEmbedded" );

                        executeAction( idplacedLayerConvertToEmbedded, undefined, DialogModes.NO );

                        theDoc.activeHistoryState = theDoc.historyStates[theDoc.historyStates.length-2];

                        return "linked"

                        }

                        catch (e) {return "embedded"};

                        } else {return "no smart object"}

                        };

                        • 9. Re: File information for smart objects
                          Level 1

                          In the past few weeks I had to do a couple of simple Photoshop projects, that though they were very different, both required some repetitive tasks. In one case I was doing the portraits of agents working at a travel agency. They all had the same treatment: a gradient background, colored rounded corners frame, and a reflection underneath. The other was making labels in two languages, each label with the same text in most areas, but custom text for the names of the addressees. At any rate, both projects were prime examples on how to use Smart Objects in Photoshop to speed up repetitive tasks.

                          In my mind, Smart Objects is one of those things in Photoshop that make the difference between a casual Photoshop user and a pro. If you don't know, or if you're not sure of what Smart Objects are, or what they do, it boils down to allowing you to edit layers non-destructively. But apart from that, they allow you to create multiple instances of the same object so when you edit the data within the smart object, all instances get updated automagically.

                          In the case of the labels, the idea was to have the same design and style, but depending on the addressees there would be an English version or a Spanish version. Here I'll explain how I did this project.

                          In a nutshell, the idea was:

                          • To have a sheet of paper divided into 2 sections (one in English and the other in Spanish)

                          • Every label would have the same thank you note either in English or Spanish

                          • The wording of the thank you note could be changed at any time, throughout the whole sheet.

                          In a normal project you could make one label in either a single layer or in a group, then duplicate that layer or group and fill the sheet with duplicates. The problem with this method is that if the text had to be changed/updated, it means going into every single duplicate and make the change there, every time. By making the text of the labels into Smart Objects, it allows us to update the text only once and the change populates every instance of the Smart Object.

                          I started with an empty letter size sheet using Photoshop's defaults.

                          Pic 1

                          Then, on the left side of document I made the Spanish version of the labels and the English on the right side. For this I made two text layers: one for Spanish and another for English.

                          Pic 2

                          Here's where things would divert from the typical copy-paste process. Before duplicating these layers I turned them to a Smart object:

                          1 - First I selected both layers in the Layers palette by Shift-clicking on both.

                          2 - Right-click on the selected layers and choose the "Convert to Smart Object" option from the long pop-up menu (make sure you right-click on the name of the layers and not on their thumbnail or you won't see the "Convert to Smart Object" option in the pop-up menu). You can also find the "Convert to Smart Object" option from Layer > Smart Objects > Convert to Smart Object in the main Photoshop menu bar.

                          Pic 3

                          You'll now see a new layer with a Smart Object icon on the bottom right corner of its thumbnail.

                          Pic 4

                          Note that I selected both text layers when I made the single Smart Object. My idea was to keep the text that would stay consistent for every label on the same smart object instead of having to manage one for English and another for Spanish. It also allows me to illustrate that you can merge multiple layers into a single Smart Object.

                          3 - Ok. Now that we have a Smart Object, we can duplicate this puppy to fill our sheet with instances of the original Smart Object. You can duplicate the Smart Object and maintain a link to the original in a couple of ways: a) drag the Smart Object layer to the "Create a new layer" icon at the bottom of the Layers palette, or b) with the Smart Object layer selected, choose Layer > New > Layer Via Copy from Photoshop's menu bar.

                          4 - With the first duplicate, I rearranged the layout of the Smart Objects on the canvas to make multiple rows of labels. Then went and repeated step 3 and 4 until I had the sheet filled with Smart Objects. By now I have 9 Smart Objects in the Layers palette, all distributed on the canvas.

                          Pic 5

                          Now, let's assume we need to update something on the text. In fact, let's go and edit a few things at once. We're going to change the copy slightly, change the font, and apply a layer style. If we were not using Smart Objects, we would need to make every edit 18 times (one per text layer - remember there is an English and a Spanish version for each row).

                          If you've never used Smart Objects, here it gets a bit confusing, but the logic is that we'll edit one Smart Object, save the changes, and that will populate across every instance. The part that was confusing to me at the beginning was were we need to save the changes to the Smart Object.

                          To make the text edits:

                          1 - First we'll need to "go into" the Smart Object. Simply double-click on any of the instances we have in the layers palette. It really doesn't matter if you double-click on the first instance, the fifth, or the last. They all point to the same data.

                          As Photoshop tries to "open" the Smart Object you will get a warning that to see your changes reflected on your main canvas you'll need to save the Smart Object once you're done.

                          Pic 6

                          Clicking OK will open the contents of the Smart Object in a new tab in Photoshop. Here you will see only the layers that make up the Smart Object. In this case, a couple of text layers, one for English and another for Spanish.

                          2 - In my case, I first updated the copy for the Spanish version. Then selected both text layers and changed the font from ITC Avant Garde Gothic Demi to New Century Schoolbook, made it bold and blue instead of black. And last I added a drop shadow to both text layers.

                          Pic 7

                          3 - At this point nothing has updated on our main Photoshop project. To get these edits applied to the main project we have to save the Smart Object. I like clicking the "X" on the tab to close the Smart Object and save it then. If you haven't saved the Smart Object when you try to close it, you should get a dialogue box asking if you want to save your changes. If you click "No" the Smart Object will close and all your edits will be discarded, so click on "Yes" to save your work.

                          Pic 8

                          In fact, every time you save the Smart Object, Photoshop updates the main project. You can see it updating if you detach the project tabs from the Photoshop workspace. I often detach the Smart Object so I can see the main project and hit Control/Command-S to save the Smart Object and see how it appears in the main project behind.

                          To tell you the truth this saving step was confusing to me because I never created a new document and I don't know where it's being saved, even when the main Photoshop project has not been even saved once. But that's how Photoshop deals with Smart Objects. It's like having another project embedded within your main project.

                          At this time the Smart Object closed but if I zoom to the text there are a couple of issues.

                          Pic 9

                          With this new font, my Spanish version spilled over to the second line, the English version is cut short on the right edge, and both are missing a few pixels at the bottom of every instance. There are a couple of reasons for these issues. One is that this new font plus the drop shadow do not fit in the space or canvas of the original 2 text layers, and the other is that I'm using Paragraph Type, so the text will try to wrap around.

                          To fix these I'll have to go back to the Smart Object by double clicking on any of them in the Layers Palette, change the canvas size of the Smart Object (Image > Canvas Size...) to make it taller and wider, and edit the size of the Paragraph Type bounding box for the Spanish version so the word "cariño" stays on the first line.

                          Pic 10 - Bad smart object

                          Pic 11 - Fixed smart object

                          After making the edits and saving the Smart Object I was able to achieve the objective. The neat thing is that I only needed to make these edits in a single Smart Object that holds the two text layers, instead of having to do the same exact edits for each of the 18 text layers in the project.

                          Pic 12

                          To finish up, I do want to mention that in most cases you do want each duplicate of a Smart Object to be linked to the original, as in my example above, so when you edit the original, it reflects in every instance. But if you need to make a new Smart Object from an existing one and you don't need/want the link to the original data, you can choose Layer > Smart Objects > New Smart Object Via Copy (instead of Layer > New > Layer Via Copy which would keep the link to the original).

                          • 10. Re: File information for smart objects
                            c.pfaffenbichler Level 9

                            One thing more: Linked Smart Objects’ paths in the XMP metadata seem to be noticeable to a Script only after the file has been saved since placing the file.

                            • 11. Re: File information for smart objects
                              JSinha Level 1

                              This works perfectly. I cannot thank you enough for your help..will try to modify my script with this code snippet. Thanks again

                              • 12. Re: File information for smart objects
                                JSinha Level 1

                                Ok..noted. Thanks for your input.

                                • 13. Re: File information for smart objects
                                  c.pfaffenbichler Level 9

                                  It is a work-around and the embedding and undoing of it can take quite some time depending on the files involved.

                                  Maybe there is an easier way that I just have not found.

                                   

                                  In any case you could post a Feature Request over at

                                  Photoshop Family Customer Community

                                  asking for adding the distinction to Photoshop’s Document Object Model or making it accessible with Action Manager code.

                                  • 14. Re: File information for smart objects
                                    Max Johnson - PlayStudios Level 1

                                    CC 2017+ has more smart object info exposed to action manager jsx. I don't have time to fix this up to work outta the gate... it's just a snip from a larger script... but it should give everyone a starting point for finding all kinds of fun stuff, like if it's a linked object and what the path to that link is.

                                     

                                    Again, this is broken out of larger code...

                                     

                                    newSmartLinkViaCopy: function ( idx, newLinkName )

                                        {

                                     

                                     

                                            // Are you a smart object?

                                            layerDesc = this.getLayerAMDescriptor(idx);

                                     

                                     

                                            if(layerDesc && layerDesc.hasKey(stringIDToTypeID('smartObject')))

                                            {

                                                // Are you a linked file and not a cloud object?

                                                var soDesc = layerDesc.getObjectValue(stringIDToTypeID('smartObject'));

                                                if( soDesc.getBoolean(stringIDToTypeID('linked')) && soDesc.getType(stringIDToTypeID('link')) == DescValueType.ALIASTYPE)

                                                {

                                                    //$.writeln (soDesc.getType(stringIDToTypeID('link')) + ", " + isLinked);

                                     

                                     

                                                    // ask smart object for "more" info

                                                    var soMoreDesc = layerDesc.getObjectValue(stringIDToTypeID('smartObjectMore'));

                                     

                                     

                                                    var srcLinkURI = String(soDesc.getPath(stringIDToTypeID('link')));

                                                    var srcFile = new File (srcLinkURI);

                                                    var searchIdx = srcFile.name.search(new RegExp(".\[^.]*$","i"));

                                     

                                     

                                                    var srcLinkExtension = srcFile.name.slice(searchIdx, srcFile.name.length);

                                                    // var srcLinkBaseName = srcFile.name.slice(0,searchIdx);

                                     

                                     

                                                    if( newLinkName === undefined )

                                                    {

                                                        newLinkName =  prompt("Enter a name for the new file to be linked...", srcFile.name, "New File Name");

                                                    }

                                     

                                     

                                                    if( newLinkName === null ){return;}

                                     

                                     

                                                    if( newLinkName.search(srcLinkExtension) === -1 )

                                                    {

                                                        newLinkName += srcLinkExtension;

                                                    }

                                     

                                     

                                                    newLinkFile = new File (srcFile.parent +"/"+ newLinkName);

                                                    if( newLinkFile.absoluteURI === srcLinkURI)

                                                    {

                                                        alert ("But... that's the same file... "+newLinkName, "Nooooooo!", true);

                                                        return;

                                                    }

                                     

                                     

                                                    // Existing file check and overwrite confirmation

                                                    if( !newLinkFile.exists || confirm("File "+newLinkName+" already exists... overwrite?",true, "Overwrite File?") )

                                                    {

                                                        var newFilePath = newLinkFile.absoluteURI;

                                                        try {

                                                            srcFile.copy(newFilePath);

                                                        } catch (e) {

                                                            return "error:copy: could not copy file "+newFilePath+":"+ e.message;

                                                        }

                                     

                                     

                                                        // Sanity check and return error if missing

                                                        newFile = new File(newFilePath);

                                                        if (! newFile.exists){

                                                            $.writeln("file not created "+ newFile.error);

                                                            return "error:copy: file not created "+newFilePath;

                                                        }

                                     

                                     

                                                        // make writeable

                                                         newFile.readonly = false;

                                     

                                     

                                                        var compAppliedID = soMoreDesc.getInteger(stringIDToTypeID('comp'));

                                                        var sizeKeyDesc = soMoreDesc.getObjectValue(charIDToTypeID('Sz  '));

                                                        var soVertList = soMoreDesc.getList(stringIDToTypeID('transform'));

                                                        //var boundsDesc = layerDesc.getObjectValue(stringIDToTypeID('boundsNoMask'));

                                     

                                     

                                                        var soWidth = sizeKeyDesc.getDouble(stringIDToTypeID('width'));

                                                        var soWidthPlaced = soVertList.getDouble(2)-soVertList.getDouble(0);

                                                        var soX = soVertList.getDouble(0);

                                                        var soScaleX = (soWidthPlaced/soWidth)*100;

                                     

                                     

                                                        var soHeight = sizeKeyDesc.getDouble(stringIDToTypeID('height'));

                                                        var soHeightPlaced = soVertList.getDouble(5)-soVertList.getDouble(1);

                                                        var soY = soVertList.getDouble(1);

                                                        var soScaleY = (soHeightPlaced/soHeight)*100;

                                     

                                     

                                                        // $.writeln(soX);

                                                        // $.writeln(soY);

                                                        // $.writeln(soScaleX);

                                                        // $.writeln(soScaleY);

                                     

                                     

                                                        var newLinkedLayer = this.placeSmartLink ( newFilePath, soX, soY, soScaleX, soScaleY );

                                     

                                     

                                                        compAppliedID = soMoreDesc.getInteger(stringIDToTypeID('comp'));

                                     

                                     

                                                        return newLinkedLayer;

                                                    }

                                                }

                                            }

                                            return null;

                                        }