8 Replies Latest reply: Apr 18, 2009 8:24 AM by Kasyan Servetsky RSS

    Need help [JS]: select images/captions in InD, switch to Bridge and write to XMP

    jhuddle123 Community Member
      I've got a javascript for CS3/Tiger that I'm writing that eventually will let a user select an image and a caption in InDesign, then tells bridge to write the caption data into the image metadata. I need help - the script works in pieces, but not when put together.

      It's built as an indesign script, then follows the procedure to create a script object to send to bridge. When it runs, the indesign portion works fine, but nothing happens in bridge.

      The script is below, with comments.

      Can someone help!? Thanks.

      // This is the InDesign portion of the script, which was tested separately and is working fine. Right now, for testing purposes, the data from this portion isn't even used by the later Bridge portion but once I get the bridge part together, it will use those variables.
      #target "indesign-5.0"
      var myObjectList = new Array;
      //Script does nothing if no documents are open or if no objects are selected.
      if(app.documents.length != 0){
      if(app.selection.length != 0){
      for(myCounter = 0; myCounter < app.selection.length; myCounter ++){
      switch(app.selection[myCounter].constructor.name){
      case "TextFrame":
      var myCaption = app.selection[myCounter].contents;
      $.writeln(myCaption);
      break;
      default:
      var myLink = app.selection[myCounter].graphics[0].itemLink.filePath;
      $.writeln(myLink);
      }
      }
      }
      }

      // here's where I create the string of the script to send to Bridge.
      // the script itself is almost completely a lifted from the SnpModifyMetadta sample from the Bridge sdk
      // it basically finds a certain image file and modifies the description metatata. i hardcoded the caption text and image path just for testing- eventually i would like it to use the variables from indesign.
      // this script works fine as-is when run in the original Bridge scripting context.
      // the method here of concatenating and sending the myScript string works fine when i use a different (smaller) script. so i can't tell why it's not working!?

      var myScript = "function SnpModifyMetadata()";
      myScript += "{";
      myScript += " this.requiredContext = \"Needs to run in Bridge, \nwith a selection of a file, \nideally with some metadata\";";
      myScript += "}";
      myScript += "SnpModifyMetadata.prototype.run = function()";
      myScript += "{";
      myScript += " if(!this.canRun())";
      myScript += " {";
      myScript += " return false;";
      myScript += " }";
      myScript += " if( xmpLib == undefined )";
      myScript += " {";
      myScript += " if( Folder.fs == \"Windows\" )";
      myScript += " {";
      myScript += " var pathToLib = Folder.startup.fsName + \"/AdobeXMPScript.dll\";";
      myScript += " }";
      myScript += " else";
      myScript += " {";
      myScript += " var pathToLib = Folder.startup.fsName + \"/AdobeXMPScript.framework\";";
      myScript += " }";
      myScript += " var libfile = new File( pathToLib );";
      myScript += " var xmpLib = new ExternalObject(\"lib:\" + pathToLib );";
      myScript += " }";
      myScript += " $.writeln(\"About to run SnpModifyXML\");";
      myScript += " var thumb = new Thumbnail(Folder(\"/Macintosh HD/Users/hearstuser/Desktop/BridgeScriptImages/orion_stack copy 6.psd\"));";
      myScript += " if(thumb.hasMetadata)";
      myScript += " {";
      myScript += " var md = thumb.synchronousMetadata;";
      myScript += " var xmp = new XMPMeta(md.serialize());";
      myScript += " $.writeln(\"SnpModifyXML: About to modify XMP data for \" + thumb.name);";
      myScript += " $.writeln(\"XMP packet before modifications:\");";
      myScript += " $.writeln(\"-------------------------------------------------\");";
      myScript += " $.writeln(xmp.dumpObject());";
      myScript += " xmp.deleteProperty(XMPConst.NS_XMP, \"Description\");";
      myScript += " xmp.setProperty(XMPConst.NS_XMP, \"Description\", \"hoThereHeyThere\");";
      myScript += " var d = new XMPDateTime(new Date());";
      myScript += " d.convertToLocalTime();";
      myScript += " xmp.setProperty(XMPConst.NS_XMP, \"ModifyDate\", d, XMPConst.XMPDATE);";
      myScript += " $.writeln(\"XMP packet af
        • 1. Re: Need help [JS]: select images/captions in InD, switch to Bridge and write to XMP
          jhuddle123 Community Member
          Here's the rest of the script (sorry so long, maybe i'm supposed to upload a file..??)

          myScript += " $.writeln(\"XMP packet after modifications:\");";
          myScript += " $.writeln(\"-------------------------------------------------\");";
          myScript += " $.writeln(xmp.dumpObject());";
          myScript += " var updatedPacket = xmp.serialize(XMPConst.SERIALIZE_OMIT_PACKET_WRAPPER | XMPConst.SERIALIZE_USE_COMPACT_FORMAT);";
          myScript += " thumb.metadata = new Metadata(updatedPacket);";
          myScript += " }";
          myScript += " else";
          myScript += " {";
          myScript += " $.writeln(\"The selected thumbnail has no metadata.\");";
          myScript += " }";
          myScript += " $.writeln(\"Ran SnpModifyMetadata\");";
          myScript += " return true;";
          myScript += "}";
          myScript += "SnpModifyMetadata.prototype.canRun = function()";
          myScript += " {";
          myScript += " if( (BridgeTalk.appName == \"bridge\") && (app.document.selectionLength == 1)) {";
          myScript += " return true;";
          myScript += " }";
          myScript += " $.writeln(\"ERROR:: Cannot run SnpModifyMetadata\");";1
          myScript += " $.writeln(this.requiredContext);";
          myScript += " return false;";
          myScript += "}";
          myScript += "if(typeof(SnpModifyMetadata_unitTest ) == \"undefined\") {";
          myScript += " new SnpModifyMetadata().run();";
          myScript += "}";

          myScript2 = "var thumb = new Thumbnail(Folder(\"/Macintosh HD/Users/hearstuser/Desktop/BridgeScriptImages/orion_stack copy 18.psd\"));";
          myScript2 += " var props = thumb.reflect.properties; for (var i = 0; i < props.length; i++) {$.writeln('this property ' + props[i].name + ' is ' + thumb[props[i].name]);}";

          //here's the script in action in bridge

          var targetApp = BridgeTalk.getSpecifier( "bridge", "2");
          if( targetApp ) {
          var bt = new BridgeTalk; // create a new BridgeTalk message object
          bt.target = "bridge"; // send this msg to the Adobe Bridge application
          // the script to evaluate is contained in a string in the "body" property
          bt.body = myScript
          bt.send(0);
          }

          Any help is appreciated!
          • 2. Re: Need help [JS]: select images/captions in InD, switch to Bridge and write to XMP
            jhuddle123 Community Member
            oh and ignore the "myScript2" section -- that's what i used just for testing (and works ok)
            • 3. Re: Need help [JS]: select images/captions in InD, switch to Bridge and write to XMP
              Kasyan Servetsky Community Member

              #target 'indesign-5.0'
              var myObjectList = new Array;
              if(app.documents.length != 0){
                 if(app.selection.length != 0){
                    for(myCounter = 0; myCounter < app.selection.length; myCounter ++){
                       switch(app.selection[myCounter].constructor.name){
                       case "TextFrame":
                       var myCaption = app.selection[myCounter].contents;
                       $.writeln(myCaption); break;
                       default: var myLink = app.selection[myCounter].graphics[0].itemLink.filePath;
                       $.writeln(myLink);
                       }
                    }
                 }
              }

              var myScript = 'function SnpModifyMetadata()\n';
              myScript += '{\n';
              myScript += ' this.requiredContext = \"Needs to run in Bridge, with a selection of a file, ideally with some metadata\";\n';
              myScript += '}\n';
              myScript += 'SnpModifyMetadata.prototype.run = function()\n';
              myScript += '{\n';
              myScript += ' if(!this.canRun())\n';
              myScript += ' {\n';
              myScript += ' return false;\n';
              myScript += ' }\n';
              myScript += ' if( xmpLib == undefined )\n';
              myScript += ' {\n';
              myScript += ' if( Folder.fs == \"Windows\")\n';
              myScript += ' {\n';
              myScript += ' var pathToLib = Folder.startup.fsName + \"/AdobeXMPScript.dll\";\n';
              myScript += ' }\n';
              myScript += ' else\n';
              myScript += ' {\n';
              myScript += ' var pathToLib = Folder.startup.fsName + \"/AdobeXMPScript.framework\";\n';
              myScript += ' }\n';
              myScript += ' var libfile = new File( pathToLib );\n';
              myScript += ' var xmpLib = new ExternalObject(\"lib:\" + pathToLib );\n';
              myScript += ' }\n';
              myScript += ' $.writeln(\"About to run SnpModifyXML\");\n';
              myScript += ' var thumb = new Thumbnail(Folder(\"~/Desktop/BridgeScriptImages/orion_stack copy 6.psd\"));\n';
              myScript += ' if(thumb.hasMetadata)\n';
              myScript += ' {\n';
              myScript += ' var md = thumb.synchronousMetadata;\n';
              myScript += ' var xmp = new XMPMeta(md.serialize());\n';
              myScript += ' $.writeln(\"SnpModifyXML: About to modify XMP data for \" + thumb.name);\n';
              myScript += ' $.writeln(\"XMP packet before modifications:\");\n';
              myScript += ' $.writeln(\"-------------------------------------------------\");\n';
              myScript += ' $.writeln(xmp.dumpObject());\n';
              myScript += ' xmp.deleteProperty(XMPConst.NS_XMP, \"Description\");\n';
              myScript += ' xmp.setProperty(XMPConst.NS_XMP, \"Description\", \"hoThereHeyThere\");\n';
              myScript += ' var d = new XMPDateTime(new Date());\n';
              myScript += ' d.convertToLocalTime();\n';
              myScript += ' xmp.setProperty(XMPConst.NS_XMP, \"ModifyDate\", d, XMPConst.XMPDATE);\n';
              myScript += ' $.writeln(\"XMP packet after modifications:\");\n';
              myScript += ' $.writeln(\"-------------------------------------------------\");\n';
              myScript += ' $.writeln(xmp.dumpObject());\n';
              myScript += ' var updatedPacket = xmp.serialize(XMPConst.SERIALIZE_OMIT_PACKET_WRAPPER | XMPConst.SERIALIZE_USE_COMPACT_FORMAT);\n';
              myScript += ' thumb.metadata = new Metadata(updatedPacket);\n';
              myScript += ' }\n';
              myScript += ' else\n';
              myScript += ' {\n';
              myScript += ' $.writeln(\"The selected thumbnail has no metadata.\");\n';
              myScript += ' }\n';
              myScript += ' $.writeln(\"Ran SnpModifyMetadata\");\n';
              myScript += ' return true;\n';
              myScript += '}\n';
              myScript += 'SnpModifyMetadata.prototype.canRun = function()\n';
              myScript += ' {\n';
              myScript += ' if( (BridgeTalk.appName == \"bridge\") && (app.document.selectionLength == 1)) {\n';
              myScript += ' return true;\n';
              myScript += ' }\n';
              myScript += ' $.writeln(\"ERROR:: Cannot run SnpModifyMetadata\");\n';1
              myScript += ' $.writeln(this.requiredContext);\n';
              myScript += ' return false;\n';
              myScript += '}\n';
              myScript += 'if(typeof(SnpModifyMetadata_unitTest ) == \"undefined\") {\n';
              myScript += ' new SnpModifyMetadata().run();\n';
              myScript += '}';

              var targetApp = BridgeTalk.getSpecifier( "bridge", "2");
              if( targetApp ) {
                 var bt = new BridgeTalk; // create a new BridgeTalk message object
                 bt.target = "bridge"; // send this msg to the Adobe Bridge application
                 bt.body = myScript
                 bt.send(0);
              }
              • 4. Re: Need help [JS]: select images/captions in InD, switch to Bridge and write to XMP
                Kasyan Servetsky Community Member
                Hi Jim,

                First of all please use Script Poster when you post your code – this will make it much easier to read.
                If you place a breakpoint somewhere in your code, after you defined myScript variable and type ‘myScript’ and return in console, you’ll get the script you are about to send to Bridge. Copy it , create a new file and paste. You’ll see that your code is totally scrambled. It can’t work.
                At first, you need to create a script for Bridge, test it and then assign it to a variable as a string.
                When you do this, use two sets of quotes single – outside and double – inside each line, and also put a new line (\n)at the end of every but last line.
                And you don’t need to place the whole example script – it looks like spaghetti code. Just write what is necessary for your script to work.

                Kasyan
                • 5. Re: Need help [JS]: select images/captions in InD, switch to Bridge and write to XMP
                  jhuddle123 Community Member
                  Kasyan, thanks for your message. I will use the Script Poster next time for sure, sorry for the mess. I did make efforts to take out superfluous code (it's not just a copy/paste of the example script), but I hope i didn't miss any.

                  The script works great now.

                  However, i'm trying to get it to the next stage, where i get the myCaption from InDesign and pass it through the BridgeTalk object. I have no success. If i understood the Javascript tools guide correctly . . I tried this:

                  bt.headers ["myNewCaption"] = "" + myCaption;

                  before the bt.send(0);

                  and then I try to test if the variable can be seen in the bridge script with this as the last line:

                  myScript += ' $.writeln(bt.headers [\"myNewCaption\"]);';

                  But after running, console shows no trace of it!

                  Actually trying to get it into this line from the middle of the Bridge script:

                  myScript += ' xmp.setProperty(XMPConst.NS_XMP, \"Description\", myCaption");\n';

                  Hope this makes sense.

                  If anyone has suggesttions let me know! Thanks.
                  • 6. Re: Need help [JS]: select images/captions in InD, switch to Bridge and write to XMP
                    Kasyan Servetsky Community Member
                    Hi Jim,

                    You don’t need to use headers like so:
                    bt.headers ["myNewCaption"] = "" + myCaption;
                    


                    Here is my vision of how to make this script – it's not totally tested and it requires improvement of course.

                    Kasyan

                    #target indesign
                    
                    var myDoc = app.activeDocument;
                    var mySelection = app.selection;

                    if (mySelection.length != 2) {
                       alert("One image and one textframe should be selected.");
                       exit();
                    }
                    try {
                       if (mySelection[0].constructor.name == "TextFrame" && mySelection[1].images.length == 1) {
                          var myTextFrame = mySelection[0];
                          var myImage = mySelection[1].images[0];
                          var myCaption = myTextFrame.contents;
                          var myPath = new File(myImage.itemLink.filePath).absoluteURI;
                       }
                       else if (mySelection[1].constructor.name != "TextFrame" && mySelection[0].images.length == 1) {
                          var myTextFrame = mySelection[1];
                          var myImage = mySelection[0].images[0];
                          var myCaption = myTextFrame.contents;
                          var myPath = new File(myImage.itemLink.filePath).absoluteURI;
                       }
                    }
                    catch(e) {
                       alert("Something wrong with your selection.\nError: " + e.message);
                       exit();
                    }

                    var bt = new BridgeTalk();
                    bt.target = "bridge";
                    var myScript = 'function WriteCaption(myCaption, myPath) {\n';
                    myScript += '    if( xmpLib == undefined )\n';
                    myScript += '    {\n';
                    myScript += '       if( Folder.fs == "Windows")\n';
                    myScript += '       {\n';
                    myScript += '          var pathToLib = Folder.startup.fsName + "/AdobeXMPScript.dll";\n';
                    myScript += '       }\n';
                    myScript += '      else\n';
                    myScript += '      {\n';
                    myScript += '          var pathToLib = Folder.startup.fsName + "/AdobeXMPScript.framework";\n';
                    myScript += '       }\n';
                    myScript += '       var libfile = new File( pathToLib );\n';
                    myScript += '       var xmpLib = new ExternalObject("lib:" + pathToLib );\n';
                    myScript += '    }\n';
                    myScript += '   var thumb = new Thumbnail (File (myPath));\n';
                    myScript += '   if (thumb.hasMetadata)\n';
                    myScript += '    {\n';
                    myScript += '      var md = thumb.synchronousMetadata;\n';
                    myScript += '      var xmp = new XMPMeta(md.serialize());\n';
                    myScript += '      xmp.deleteProperty(XMPConst.NS_XMP, "Description");\n';
                    myScript += '      xmp.setProperty(XMPConst.NS_XMP, "Description", myCaption);\n';
                    myScript += '      var updatedPacket = xmp.serialize(XMPConst.SERIALIZE_OMIT_PACKET_WRAPPER | XMPConst.SERIALIZE_USE_COMPACT_FORMAT);\n';
                    myScript += '      thumb.metadata = new Metadata(updatedPacket);\n';
                    myScript += '   }\n';
                    myScript += '}\n';
                    myScript += 'WriteCaption(\"' + myCaption + '\", \"' + myPath + '\");\n';
                    bt.body = myScript;
                    bt.send();
                    • 7. Re: Need help [JS]: select images/captions in InD, switch to Bridge and write to XMP
                      jhuddle123 Community Member

                      Well this is great - very informative. Thanks so much!

                      • 8. Re: Need help [JS]: select images/captions in InD, switch to Bridge and write to XMP
                        Kasyan Servetsky Community Member

                        Recently I received an e-mail from a user asking me to remake the script I had posted here a while ago.

                        I rechecked my previous post and found a couple of serious mistakes. So I revised it according to what he asked. Each page in his InDesign document contains one image and one text frame with description.

                        At start the script checks the selection. If nothing is selected it processes all pages in the document. In case two objects are selected – it checks whether one of them text frame and the other a rectangle and if the rectangle contains an image, then it reads the contents of the text frame, replacing hard and soft returns with spaces and writes this text to the corresponding image’s description field.

                        In the end, it updates all links and saves the file.

                        The script works both on Mac and PC, assuming that CS3 installed in the default location so long as it depends on libraries:

                        /C/Program Files/Adobe/Adobe Bridge CS3/AdobeXMPScript.dll (on PC)

                        /Applications/Adobe Bridge CS3/Bridge CS3.app/Contents/MacOS/AdobeXMPScript.framework (on MAC)

                        So, if necessary, correct these lines.

                         

                        Here is my script in case somebody is interested:

                         

                        #target indesign

                         

                        var myDoc = app.activeDocument;
                        var mySelection = app.selection;

                         

                        if (mySelection.length == 0) {
                            ProcessPages();
                        }
                        else if (mySelection.length == 1) {
                            alert("One image and one textframe should be selected.");
                            exit();   
                        }
                        else if (mySelection.length == 2) {
                            try {
                                if (mySelection[0].constructor.name == "TextFrame" && mySelection[1].images.length == 1) {
                                    var myTextFrame = mySelection[0];
                                    var myImage = mySelection[1].images[0];
                                    var myCaption = ReplaceReturnWithSpace(myTextFrame.contents);
                                    var myPath = new File(myImage.itemLink.filePath).absoluteURI;
                                }
                                else if (mySelection[1].constructor.name == "TextFrame" && mySelection[0].images.length == 1) {
                                    var myTextFrame = mySelection[1];
                                    var myImage = mySelection[0].images[0];
                                    var myCaption = ReplaceReturnWithSpace(myTextFrame.contents);
                                    var myPath = new File(myImage.itemLink.filePath).absoluteURI;
                                }
                            }
                            catch(e) {
                                alert("Something wrong with your selection.\nError: " + e.message);
                                exit();
                            }

                         

                            CreateBridgeTalkMessage(myCaption, myPath);
                        }
                        else if (mySelection.length > 2) {
                            alert("One image and one textframe should be selected.");
                            exit();
                        }

                         

                        UpdateAllOutdatedLinks();
                        myDoc.save();
                        alert("All done.");

                         

                        //+++++++++++++++++++ FUNCTIONS ++++++++++++++++++++++

                         

                        function CreateBridgeTalkMessage(myCaption, myPath) {
                            var bt = new BridgeTalk();
                            bt.target = "bridge";
                            var myScript = WriteCaption.toString() + '\r';
                            myScript += 'WriteCaption(\"' + myCaption + '\", \"' + myPath + '\");';
                            bt.body = myScript;
                            bt.send();
                        }

                         

                        function WriteCaption(myCaption, myPath) {
                            if( xmpLib == undefined ) {
                                if( Folder.fs == "Windows") {
                                    var pathToLib = "/C/Program Files/Adobe/Adobe Bridge CS3/AdobeXMPScript.dll";
                                }
                                else {
                                    var pathToLib = "/Applications/Adobe Bridge CS3/Bridge CS3.app/Contents/MacOS/AdobeXMPScript.framework";
                                }
                                var libfile = new File( pathToLib );
                                var xmpLib = new ExternalObject("lib:" + pathToLib );
                            }
                            var thumb = new Thumbnail (File (myPath));

                         

                            if (thumb.hasMetadata) {
                                var md = thumb.synchronousMetadata;
                                var xmp = new XMPMeta(md.serialize());
                                xmp.deleteProperty(XMPConst.NS_XMP, "Description");
                                xmp.setProperty(XMPConst.NS_XMP, "Description", myCaption);
                                var updatedPacket = xmp.serialize(XMPConst.SERIALIZE_OMIT_PACKET_WRAPPER | XMPConst.SERIALIZE_USE_COMPACT_FORMAT);
                                thumb.metadata = new Metadata(updatedPacket);
                            }
                        }

                         

                        function ProcessPages() {
                            var myPages = myDoc.pages;
                            for (i = 0; i < myPages.length; i++) {
                                var myPage = myPages[i];
                                if (myPage.textFrames.length == 1 && myPage.rectangles.length == 1) {
                                    if (myPage.rectangles[0].graphics.length == 1) {
                                    var a = myPage.rectangles[0];
                                        var myImage = myPage.rectangles[0].graphics[0];
                                        var myPath = new File(myImage.itemLink.filePath).absoluteURI;
                                        var myTextFrame = myPage.textFrames[0];
                                        var myCaption = ReplaceReturnWithSpace(myTextFrame.contents);
                                        CreateBridgeTalkMessage(myCaption, myPath);
                                    }
                                }
                            }
                        }

                         

                        function ReplaceReturnWithSpace(myString) {
                            myString = myString.replace( /\n/g , " " );
                            myString = myString.replace( /\r/g , " " );
                            return myString;
                        }

                         

                        function UpdateAllOutdatedLinks() {
                            for(var myCounter = myDoc.links.length-1; myCounter >= 0; myCounter--){
                                var myLink = myDoc.links[myCounter];
                                if (myLink.status == LinkStatus.linkOutOfDate){
                                    myLink.update();
                                }
                            }
                        }

                         

                        If somebody interested in developing this script further, do not hesitate to post here your feedback.

                         

                        Kasyan