Skip navigation
Currently Being Moderated

Bridge talk for InDesign CS4/CS5 js

Dec 2, 2010 8:45 PM

Hi,

 

Have this code that opens file in Ph and resaves it in different format. In this case I need to adjust it to open pdf file in photoshop and set crop to media box. It works with pdf files as is, but if there is white space around artwork, it gets removed. How can I set it to open with media box:

 

 

function ResaveInPS(myImagePath, myNewPath) {
     try {
           var myPsDoc = app.open(new File(myImagePath));
             if (myPsDoc.mode == DocumentMode.CMYK) {
                    myPsDoc.changeMode(ChangeMode.RGB);
               }
           var docName = myPsDoc.name;
 
          var myPNGSaveOptions = new PNGSaveOptions();
          myPNGSaveOptions.interlaced = false; // or true
          myPsDoc.saveAs(new File(myNewPath), myPNGSaveOptions, true);
          myPsDoc.close(SaveOptions.DONOTSAVECHANGES);      
     }
     catch (err) {
          try {
               app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
          }
          catch (err) {}
     }
}

 

 

Thank you for your help.

Yulia

 
Replies
  • Currently Being Moderated
    Dec 3, 2010 1:13 AM   in reply to Yuliaart

    In the same manner as you have made PNG save options object for saving. You need to have Photoshop make a PDF open options object to open the file in the required way else you will get default using just File and open. Color Space is one of the properties so you could then remove your mode check too by doing this…

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 3, 2010 2:39 PM   in reply to Yuliaart

    Mark was probably thinking about Illustrator. Indesign has a PDFPlacePreferences which addresses the pdfCrop which can be set to a number of options. Look in Jongware's html version of the OMV.

     

    http://www.jongware.com/idjshelp.html

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 4, 2010 10:39 AM   in reply to Yuliaart
    function ResaveInPS(myImagePath, myNewPath) {
         try {
                    if (myImagePath.match(/\.pdf$/) != null) {
                        var pdfOpenOptions = new PDFOpenOptions;
                        pdfOpenOptions.cropPage = CropToType.MEDIABOX;
                        pdfOpenOptions.resolution = 72;
                        pdfOpenOptions.usePageNumber = true;
                   }
               var myPsDoc = app.open(new File(myImagePath));
                 if (myPsDoc.mode == DocumentMode.CMYK) {
                        myPsDoc.changeMode(ChangeMode.RGB);
                   }
               var docName = myPsDoc.name;
     
              var myPNGSaveOptions = new PNGSaveOptions();
              myPNGSaveOptions.interlaced = false; // or true
              myPsDoc.saveAs(new File(myNewPath), myPNGSaveOptions, true);
              myPsDoc.close(SaveOptions.DONOTSAVECHANGES);      
         }
         catch (err) {
              try {
                   app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
              }
              catch (err) {}
         }
    }
    

     

    PDFOpenOptions can be found in Photoshop JavaScript reference (or OMV) -- not in InDesign OMV -- and it has several properties  corresponding to the settings in the Photoshop's "Import PDF" dialog box. The property you are interested in is called cropPage:

     

    var pdfOpenOptions = new PDFOpenOptions;
    pdfOpenOptions.cropPage = CropToType.MEDIABOX;
    

     

    Kasyan

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 5, 2010 5:51 AM   in reply to Kasyan Servetsky

    Kasyan, you have the correct property for the PDF open options which you create… but then you don't pass those options as parameter open command? You can also as I said choose the opening mode…

     

    // Record then change user prefs
    var uDD = app.displayDialogs;
    app.displayDialogs = DialogModes.NO;
     
    var pdfOpenOptions = new PDFOpenOptions();
     
    pdfOpenOptions.antiAlias = true;
    pdfOpenOptions.bitsPerChannel = BitsPerChannelType.EIGHT;
    pdfOpenOptions.cropPage = CropToType.MEDIABOX;
    pdfOpenOptions.mode = OpenDocumentMode.RGB;
    pdfOpenOptions.page = '1';
    pdfOpenOptions.resolution = 72;
    pdfOpenOptions.suppressWarnings = true;
    pdfOpenOptions.usePageNumber = true;
     
    var myPsDoc = app.open(new File('~/Desktop/Testing.pdf'), pdfOpenOptions);
     
    // The rest… save, close, etc…
     
    // Put back prefs
    app.displayDialogs = uDD;
    

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 5, 2010 8:09 AM   in reply to Muppet Mark-QAl63s

    Hi Muppet Mark,

     

    This function is a part of a larger script that was discussed in this thread. I set dialog mode there but in a separate function.

    but then you don't pass those options as parameter open command?

    Ouch! I know this but made a typo. Thank you for pointing this out.

    Of course, this line should be:

    var myPsDoc = app.open(new File(myImagePath), pdfOpenOptions);
    

     

    Kasyan

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 5, 2010 8:16 AM   in reply to Kasyan Servetsky

    Kasyan, I presumed this was just a typo error… Didn't look at the other post regarding the dialogs. More trying to point out the 'OpenDocumentMode.RGB' then you don't need to check n change… May as well use the option…

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 5, 2010 8:42 AM   in reply to Muppet Mark-QAl63s

    But this script opens and resaves other formats too -- for example JPEGs --  not only PDFs. And the OP wants them to be resaved to PNG format, but this is impossible if the image is in CMYK. That's why the script checks the color mode for all images it opens instead of using the 'OpenDocumentMode.RGB' which would work only for PDFs.

     

    Kasyan

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 7, 2010 3:35 AM   in reply to Yuliaart

    Yulia, 'interlaced' is NOT a property of the TIFF save options you have switched the save options object PNG to TIFF but NOT the required properties of that format…

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 7, 2010 10:44 AM   in reply to Yuliaart

    Yulia, I don't quite understand what the white space around the art piece is. What setting in Photoshop influences it?

     

    Kasyan

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 8, 2010 1:29 AM   in reply to Yuliaart

    It's difficult for me to give a definite advice without seeing what's going on in your file. I tried to recreate you problem but couldn't. May be flattening the image would solve the issue?

    var myPsDoc = app.open(new File(myImagePath), pdfOpenOptions);
    myPsDoc.flatten();
    

     

    Kasyan

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 8, 2010 4:40 AM   in reply to Kasyan Servetsky

    Kasyan, I too think the movement on replace is caused by a difference in the bounds… The PDF using pre-defined boxes. I suspect the TIF is NOT flat and that ID is using the non-transparent area as the new bounds… on replace? You could either flatten in the document or using the TIF options…

     

     

    function ResaveInPS(myImagePath, myNewPath) {
         try {
              if (myImagePath.match(/\.pdf$/) != null) {
                   var pdfOpenOptions = new PDFOpenOptions;
                   pdfOpenOptions.cropPage = CropToType.MEDIABOX;
                   pdfOpenOptions.resolution = 300;
                   pdfOpenOptions.usePageNumber = true;
              }
              var myPsDoc = app.open(new File(myImagePath), pdfOpenOptions);
              if (myPsDoc.mode != DocumentMode.CMYK) {
                   myPsDoc.changeMode(ChangeMode.CMYK);
              }
              myPsDoc.flatten(); // or layers false…
              
              var myTiffSaveOptions  = new TiffSaveOptions();
              
              myTiffSaveOptions.alphaChannels = false;
              myTiffSaveOptions.byteOrder = ByteOrder.MACOS;
              myTiffSaveOptions.embedColorProfile = true;
              myTiffSaveOptions.imageCompression = TIFFEncoding.TIFFLZW;
              myTiffSaveOptions.layers = true; // Or false here…
              myTiffSaveOptions.spotColors = true;
              myTiffSaveOptions.transparency = true;
              
              myPsDoc.saveAs(new File(myNewPath), myTiffSaveOptions);
              app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
         } catch (err) {}
    }
     
    
     
    |
    Mark as:
  • Currently Being Moderated
    Dec 9, 2010 9:45 AM   in reply to Yuliaart

    I think, Kasyan, the reason you don't see it might be that if dialog box in Ph set up to Media already, it will remember the settings until ph is restarted. But if you have it preset to Bounding box, let's say, does it change it for you to Media box.

    The Crop To is set to Bounding Box by default and the script changes it to Media box, as expected. I tested your script but the white space is not removed for me. Could you post sample files (or send them to my e-mail address: askoldich [at] yahoo [dot] com) so I could see what's going on in your document?

     

    Kasyan

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 11, 2010 12:20 PM   in reply to Yuliaart

    Hi Yulia,

     

    I adjusted the script:

    #target indesign
    var myDoc = app.activeDocument;
    var myDocPath = myDoc.filePath;
    var myLivePath = myDoc.filePath;
    var myFolder = new Folder(myLivePath);
     
    OpenFiles();
    UpdateAllOutdatedLinks();
    //~ myLink.editOriginal();
    //alert("Done");
     
    function OpenFiles(){
        var myFile= app.selection[0];
        try{
            if(myFile.isValid == true){}
        }
        catch (e){
        alert ("Please choose PDF and rerun the script");
            exit();
         }
     
        if(myFile.constructor.name == "PDF"){
            myLink = myFile.itemLink;
        }
        else{if(myFile.constructor.name == "Rectangle"){
            myLink = myFile.graphics[0].itemLink;
        }
        else{
            alert ("The artwork has to be PDF file. Please choose correct artwork and rerun the script");
            exit();
        }
        }
          
              if (myLink.name.toLowerCase().indexOf(".pdf") > -1){
                   var myImage = myLink.parent;
                   var myImagePath = myLink.filePath;
                   var myImageFile = new File(myImagePath);
                   var myNewPath =  myFolder.absoluteURI + "/" + GetFileNameOnly(myImageFile.name) + ".tif";
                   CreateBridgeTalkMessage(myImagePath, myNewPath);
                   Relink(myLink, myNewPath);
              }
    }
    //--------------------------------------------------------------------------------------------------------------
    function CreateBridgeTalkMessage(myImagePath, myNewPath) {
         var bt = new BridgeTalk();
         bt.target = "photoshop";
         
         var myScript = 'app.displayDialogs = DialogModes.NO;\r';
         myScript += 'try {\r';
         myScript += 'if ("' + myImagePath + '".match(/\.pdf$/) != null) {\r';
         myScript += '    var pdfOpenOptions = new PDFOpenOptions();\r';
         myScript += '    pdfOpenOptions.cropPage = CropToType.MEDIABOX;\r';
         myScript += '    pdfOpenOptions.resolution = 300;\r';
         myScript += '    pdfOpenOptions.usePageNumber = true;\r';
         myScript += '    var myPsDoc = app.open(new File("' + myImagePath  + '"), pdfOpenOptions);\r';     
         myScript += '}\r';
         myScript += 'else {\r';
         myScript += '    var myPsDoc = app.open(new File(myImagePath));\r';
         myScript += '}\r';
         myScript += 'if (myPsDoc.mode != DocumentMode.CMYK) {\r';
         myScript += '    myPsDoc.changeMode(ChangeMode.CMYK);\r';
         myScript += '}\r';
         myScript += 'myPsDoc.flatten();\r';
         myScript += 'var docName = myPsDoc.name;\r';
         myScript += 'var myTiffSaveOptions  = new TiffSaveOptions();\r';
         myScript += 'myTiffSaveOptions.alphaChannels = false;\r';
         myScript += 'myTiffSaveOptions.byteOrder = ByteOrder.MACOS;\r';
         myScript += 'myTiffSaveOptions.embedColorProfile = true;\r';
         myScript += 'myTiffSaveOptions.imageCompression = TIFFEncoding.TIFFLZW;\r';
         myScript += 'myTiffSaveOptions.layers = true;\r';
         myScript += 'myTiffSaveOptions.spotColors = false;\r';
         myScript += 'myTiffSaveOptions.transparency = true;\r';
         myScript += 'myPsDoc.saveAs(new File("' + myNewPath + '"), myTiffSaveOptions);\r';
         myScript += 'myPsDoc.close(SaveOptions.DONOTSAVECHANGES);\r';
         myScript += '}\r';
         myScript += 'catch (err) {\r';
         myScript += '    try {\r';
         myScript += '        app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);\r';
         myScript += '    }\r';
         myScript += '    catch (err) {}\r';
         myScript += '}\r';
         myScript += 'app.displayDialogs = DialogModes.ALL;\r';
    //~      $.writeln(myScript);
         bt.onResult = function(resObj) {} 
         bt.send(100);
         }
    //--------------------------------------------------------------------------------------------------------------
    function ResaveInPS(myImagePath, myNewPath) {
        try {
                   if (myImagePath.match(/\.pdf$/) != null) {
                       var pdfOpenOptions = new PDFOpenOptions;
                       pdfOpenOptions.cropPage = CropToType.MEDIABOX;
                       pdfOpenOptions.resolution = 300;
                       pdfOpenOptions.usePageNumber = true;
                  }
              var myPsDoc = app.open(new File(myImagePath), pdfOpenOptions);
                if (myPsDoc.mode != DocumentMode.CMYK) {
                       myPsDoc.changeMode(ChangeMode.CMYK);
                  }
              myPsDoc.flatten(); // or layers false…
              
              var docName = myPsDoc.name;
     
             var myTiffSaveOptions  = new TiffSaveOptions();
             myTiffSaveOptions.alphaChannels = false;
              myTiffSaveOptions.byteOrder = ByteOrder.MACOS;
              myTiffSaveOptions.embedColorProfile = true;
              myTiffSaveOptions.imageCompression = TIFFEncoding.TIFFLZW;
              myTiffSaveOptions.layers = true; // Or false here…
              myTiffSaveOptions.spotColors = false;
              myTiffSaveOptions.transparency = true;
     
             myPsDoc.saveAs(new File(myNewPath), myTiffSaveOptions);
             myPsDoc.close(SaveOptions.DONOTSAVECHANGES);
        }
        catch (err) {
             try {
                  app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
             }
             catch (err) {}
        }
    }
    //--------------------------------------------------------------------------------------------------------------
    function Relink(myLink, myNewPath){
         var newFile = new File (myNewPath);
         if (newFile.exists) {
              var originalLinkFile = new File(myLink.filePath);
              myLink.relink(newFile);
              
              try { // for versions prior to 6.0.4
                   var myLink = myLink.update();
              }
              catch(err) {}
         }
    }
    //--------------------------------------------------------------------------------------------------------------
    function UpdateAllOutdatedLinks() {
         for (var myCounter = myDoc.links.length-1; myCounter >= 0; myCounter--) {
              var myLink = myDoc.links[myCounter];
              if (myLink.status == LinkStatus.linkOutOfDate) {
                   myLink.update();
              }
         }
    }
    //--------------------------------------------------------------------------------------------------------------
    function GetFileNameOnly(myFileName) {
         var myString = "";
         var myResult = myFileName.lastIndexOf(".");
         if (myResult == -1) {
              myString = myFileName;
         }
         else {
              myString = myFileName.substr(0, myResult);
         }
         return myString;
    }
    

    Kasyan

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 12, 2010 3:04 AM   in reply to Yuliaart

     

    ...it was missing one little line...

    Oops! I just wanted to remove some unnecessary stuff after testing to clean up the script and took out the line by chance.

     

    But I've discovered one interesting fact while solving the problem: when the Photoshop script is send as a string it works as expected, but when I'm wrapping the main code in a function via toString() or toSource(), it doesn't.

    Let's make a little test: download this sample file to the desktop, start InDesign and Photoshop and run the code below.

     

    This works as expected — Crop To: Media Box is applied in Photoshop.

    2.png

    #target indesign
    CreateBridgeTalkMessage("~/Desktop/toysForUs.pdf");
     
    function CreateBridgeTalkMessage(myImagePath) {
         var bt = new BridgeTalk();
         bt.target = "photoshop";
         var myScript = 'var pdfOpenOptions = new PDFOpenOptions;\r';
         myScript += 'pdfOpenOptions.cropPage = CropToType.MEDIABOX;\r';
         myScript += 'pdfOpenOptions.resolution = 300;\r';
         myScript += 'pdfOpenOptions.usePageNumber = true;\r';
         myScript += 'app.displayDialogs = DialogModes.NO;\r';
         myScript += 'var myPsDoc = app.open(new File("' + myImagePath + '"), pdfOpenOptions);\r';
         myScript += 'app.displayDialogs = DialogModes.ALL;\r';
         bt.body = myScript;
         bt.onResult = function(resObj) {}
         bt.send(100);
    }
    

     

    But the following code doesn't — the CropToType.MEDIABOX setting is ignored:

    1.png

    #target indesign
    CreateBridgeTalkMessage("~/Desktop/toysForUs.pdf");
     
    function CreateBridgeTalkMessage(myImagePath) {
         var bt = new BridgeTalk();
         bt.target = "photoshop";
         var myScript = ResaveInPS.toString() + "\r";
         myScript += "ResaveInPS(\"" + myImagePath + "\");";
         bt.body = myScript;
         bt.onResult = function(resObj) {}
         bt.send(100);
    }
     
    function ResaveInPS(myImagePath) {
            if (myImagePath.match(/\.pdf$/) != null) {
                 var pdfOpenOptions = new PDFOpenOptions;
                 pdfOpenOptions.cropPage = CropToType.MEDIABOX;
                   pdfOpenOptions.resolution = 300;
                 pdfOpenOptions.usePageNumber = true;
           }
                   
         app.displayDialogs = DialogModes.NO;
         var myPsDoc = app.open(new File(myImagePath), pdfOpenOptions);
         app.displayDialogs = DialogModes.ALL;
    }
    

    It doesn't work with toSource() as well:

    #target indesign
    CreateBridgeTalkMessage("~/Desktop/toysForUs.pdf");
     
    function CreateBridgeTalkMessage(myImagePath) {
         var bt = new BridgeTalk();
         bt.target = "photoshop";
         var myScript = "ResaveInPS = " + ResaveInPS.toSource() + "\r";
         myScript += "ResaveInPS(\"" + myImagePath + "\");";
         bt.body = myScript;
         bt.send(100);
    }
     
    function ResaveInPS(myImagePath) {
            if (myImagePath.match(/\.pdf$/) != null) {
                 var pdfOpenOptions = new PDFOpenOptions;
                 pdfOpenOptions.cropPage = CropToType.MEDIABOX;
                   pdfOpenOptions.resolution = 300;
                 pdfOpenOptions.usePageNumber = true;
           }
                   
         app.displayDialogs = DialogModes.NO;
         var myPsDoc = app.open(new File(myImagePath), pdfOpenOptions);
         app.displayDialogs = DialogModes.ALL;
    }
    

     

    I found this post by Jongware, as far as I understand, explaining the cause of this phenomenon — but couldn’t get the meaning of it (I am not a programmer by background have no idea what "single static buffer" is)

    Could anybody shed light on this issue?

    Does it mean that using a plain string in body parameter is more reliable than a function with toString()/toSource()?

     

    Kasyan

     
    |
    Mark as:
  • John Hawkinson
    5,572 posts
    Jun 25, 2009
    Currently Being Moderated
    Mar 13, 2011 2:38 PM   in reply to Kasyan Servetsky

    Hey, Kasyan!

     

    I had meant to look into this for quite a while, because it made me very suspicious.

    It shouldn't be the case that the .toString() method gives you different results, and it's bad practice to write functions in strings instead of writing them out properly and then converting to a string. It's much better to let the compiler check your code for you, much more readable, etc., etc.

     

    Anyhow, your two examples don't do the same thing! One of them does a regexp match for /\.pdf$/ and the other doesn't. And it turns out the regexp match fails, when you would not expect it to.

     

    It turns out there's an Adobe bug there!

     

    Try this:

         var bt = new BridgeTalk();
         bt.target = "photoshop";
         bt.body = "alert(/\\.pdf$/)";
         alert(bt.body);
         bt.onResult = function(resObj) {}
         bt.send(100);
    

     

    There are two backslashes because inside a quoted ("x") string, two backslashes represent one backslash.

    The first alert, in InDesign, shows that the backslash was properly removed:

    id.png

     

    but then when it gets transmitted via BridgeTalk to PhotoShop, the backslash gets doubled!:

    ps.png

     

    That's not supposed to happen. It appears that regular expression literals get munged in BridgeTalk.

    If instead you used

         bt.body = "alert(new RegExp('\\.pdf'))";
    

     

    Then it works fine.

     

    Incidently, this has nothing to do with the single static buffer concern. That is simply the idea that if you try to run two instances of some method that uses a single buffer at the same time, they scramble over each other's workspace and get confused. Allegedly .toSource() is such a method, so if you tried to call .toSource() twice in the same variable assignment where they could conceivably happen at the same time, then you might have this problem.

    Something like:

    var result = one.toSource() + two.toSource();
    

     

    But I don't believe it has any applicability here.

     

     

    All the above tests under OSX with CS5.

     
    |
    Mark as:
  • Currently Being Moderated
    Mar 22, 2011 3:53 AM   in reply to John Hawkinson

    Hi John,

     

    I am sorry for the late reply, I have been denied access to the forum for about a week. Thanks Harbs for the advice to trash cookies — now I am back at last.

     

    Thank you very much for making the investigation. It turns out that solution to this mysterious enigma is so simple!

    I often write inter-application scripts using BridgeTalk so your discovery is very important to me.

     

    Thank you again.

     

    Kasyan

     

    P.S. Below I am posting corrected code in case somebody is interested.

     

    toSource()

    #target indesign
    CreateBridgeTalkMessage("~/Desktop/toysForUs.pdf");
     
    function CreateBridgeTalkMessage(myImagePath) {
         var bt = new BridgeTalk();
         bt.target = "photoshop";
         var myScript = "ResaveInPS = " + ResaveInPS.toSource() + "\r";
         myScript += "ResaveInPS(\"" + myImagePath + "\");";
         bt.body = myScript;
         bt.send(100);
    }
     
    function ResaveInPS(myImagePath) {
            if (myImagePath.match(new RegExp('\.pdf')) != null) {
                   var pdfOpenOptions = new PDFOpenOptions;
                   pdfOpenOptions.cropPage = CropToType.MEDIABOX;
                   pdfOpenOptions.resolution = 300;
                   pdfOpenOptions.usePageNumber = true;
           }
                   
         app.displayDialogs = DialogModes.NO;
         var myPsDoc = app.open(new File(myImagePath), pdfOpenOptions);
         app.displayDialogs = DialogModes.ALL;
    }
    

     

    toString()

    #target indesign
    CreateBridgeTalkMessage("~/Desktop/toysForUs.pdf");
     
    function CreateBridgeTalkMessage(myImagePath) {
         var bt = new BridgeTalk();
         bt.target = "photoshop";
         var myScript = ResaveInPS.toString() + "\r";
         myScript += "ResaveInPS(\"" + myImagePath + "\");";
         bt.body = myScript;
         bt.onResult = function(resObj) {}
         bt.send(100);
    }
     
    function ResaveInPS(myImagePath) {
            if (myImagePath.match(new RegExp('\.pdf')) != null) {
                 var pdfOpenOptions = new PDFOpenOptions;
                 pdfOpenOptions.cropPage = CropToType.MEDIABOX;
                   pdfOpenOptions.resolution = 300;
                 pdfOpenOptions.usePageNumber = true;
           }
                   
         app.displayDialogs = DialogModes.NO;
         var myPsDoc = app.open(new File(myImagePath), pdfOpenOptions);
         app.displayDialogs = DialogModes.ALL;
    }
    
     
    |
    Mark as:
  • John Hawkinson
    5,572 posts
    Jun 25, 2009
    Currently Being Moderated
    Mar 22, 2011 4:08 AM   in reply to Kasyan Servetsky

    Whoops, I had meant to file a bug on this...has anybody done so yet?

     

    Anyhow, I don't think this code is quite right:

     

    if (myImagePath.match(new RegExp('\.pdf')) != null) {

     

    because inside a quoted string, you need to double the backslash. Otherwise it only escapes the next character (such as \n), and a period does not need escaping. So the above is the same as Regexp('.pdf') which isn't what you want. And without a terminal dollar-sign (whoops -- it looks like I lost the $ somewhere along the way in my last example), that case will actually happen, say a file called "mypdfproblem.jpg."

     

    So I think you really want RegExp('\\.pdf$').

     
    |
    Mark as:
  • Currently Being Moderated
    Mar 22, 2011 4:48 AM   in reply to John Hawkinson

    whoops -- it looks like I lost the $ somewhere along the way in my last example

    I forgot about it too.

    because inside a quoted string, you need to double the backslash.

    At first I tried to use two backslashes before the period but it didn't work.

    For example:

    #target indesign
    CreateBridgeTalkMessage("~/Desktop/toysForUs.pdf");
     
    function CreateBridgeTalkMessage(myImagePath) {
         var bt = new BridgeTalk();
         bt.target = "photoshop";
         var myScript = "Test = " + Test.toSource() + "\r";
         myScript += "Test(\"" + myImagePath + "\");";
         bt.body = myScript;
         bt.send(100);
    }
     
    function Test(myImagePath) {
          $.writeln( myImagePath.match(new RegExp('\\.pdf$')) != null );
    }
    

    It writes false to console.

     

    If I change new RegExp('\.pdf$') to new RegExp('\\.pdf$') or to new RegExp('.pdf$'), it writes true.

     

    Kasyan

     
    |
    Mark as:
  • Currently Being Moderated
    Mar 23, 2011 5:32 PM   in reply to Kasyan Servetsky

    Umm.

    Right.

    I guess we knew that backslashes in regexp literals were broken and now we know they are broken in string literals as well:

     

    Adobe InDesign:  .
    Adobe InDesign:  \.
    Result: undefined
    Adobe Photoshop:  \.
    Adobe Photoshop:  \.
    

     

    From this code;

     

    (function() {
        var bt = new BridgeTalk();
        bt.target = "photoshop";
        bt.body ="var s = '\\.'; $.writeln(app.name+':  '+s)";
        eval(bt.body);
        bt.send(100);
        
        bt.body ="var s = String.fromCharCode(92)+'.';"+
            " $.writeln(app.name+':  '+s)";
        eval(bt.body);
        bt.send(100);   
    }());
    

     

    So, what we learn here is that if we actually care about writing functions that the JavaScript compiler interprets and than using .toString() on them [or .toSource()], then we can't use backslash literals in them, either in strings or in regexps, because BrigeTalk screws them up.

     

    Of course you can do what you did in your example -- use one backslash and expect BridgeTalk to convert it to two -- but that's a horrible idea, since part of the point of writing the function as a function is so you can test it natively. And with the backslash problem you just can't do that!

     

    So I suspect the only reasonable thing to do is to not use backslashes in string or regexp literals that will go through Bridgetalk.

    So if you need a backslash, you could construct it, such as

     

    var bs = String.fromCharCode(92);
    

     

    which is ugly but seems to me much better than either of the two alternatives: (1) writing functions that can't be properly executed in the local (non-Bridgetalk) environment because they need their backslashes doubled by BridgeTalk (2) writing functions directly as strings and knowing that their backslashes will be doubled, but since they are strings no one is likely to test them in the local environment because people don't tend to "eval" things.

     

    Does anyone have a better alternative?

     
    |
    Mark as:
  • Currently Being Moderated
    Mar 24, 2011 2:07 AM   in reply to John Hawkinson-NXenhA

    Thanks for clarifying things up, John.

    Now I know what caused the problem and how to avoid it in the future so I hope I'll never stumble into this pitfall yet again.

     

    Kasyan

     
    |
    Mark as:
  • John Hawkinson
    5,572 posts
    Jun 25, 2009
    Currently Being Moderated
    Mar 24, 2011 5:26 AM   in reply to Kasyan Servetsky

    If only that were true -- it seems like the kind of thing that one is likely to repeatedly stumble upon unless there is some error-checking to make sure it doesn't happen. I mean, one would like to be able to write nearly arbitrary functions and then .toString() them into a BridgeTalk message. Perhaps [??] something like

     

    bt.body = myFunction.toString()+"whatever";
    if (bt.body.indexOf("\\") !== -1) {
      alert("BridgeTalk message contains a backslash.\n"+
        "It will behave differently on the "+
        "other side of the world! Aborting!");
      throw "backslashError";
    }
    

     

     

    I don't know. Incidently, some shorter ways to write a backslash without using one:

     

    var bs = decodeURI("%5C");
    var bs = unescape("%u005c");
    

     

    I guess this suggests that perhaps a better plan is to always encode? Like:

     

    bt.body = "eval(unescape("+escape(myFunction)+")); whatever";
    

     

    (I haven't tested that). It adds some overhead and maybe new problems, but at least it avoids the goofy backslash problem and explicit workarounds for it? [???]

     
    |
    Mark as:
  • Currently Being Moderated
    Mar 24, 2011 7:01 AM   in reply to John Hawkinson

    Both decodeURI("%5C") and unescape("%u005c") work for me.

     

    One more silly question:

    Why did you add the throw "backslashError"; line to the code above? What is the purpose of it?

    ESTK just stops on this line and shows 'JavaScript exception thrown' at the bottom.

     

    Kasyan

     
    |
    Mark as:
  • John Hawkinson
    5,572 posts
    Jun 25, 2009
    Currently Being Moderated
    Mar 24, 2011 7:17 AM   in reply to Kasyan Servetsky

    In that example, the code throws a JavaScript error and does not let you continue if the function contains a literal backslash

     

    This is intentional, because literal backslashes cannot possibly work correctly, so the only solution if one is detected is to give up and complain.

     

    (Yes, both decodeURI() and unescape() were examples of shorter ways to write the same thing as String.fromCharCode()).

     
    |
    Mark as:
  • Currently Being Moderated
    Mar 27, 2011 12:43 AM   in reply to John Hawkinson

     

     

    From time to time I have to write quite complex scripts using BridgeTalk. Long ago I new that it's possible to pass complex objects -- arrays, DOM and custom objects -- via BridgeTalk, but only recently I applied this in practice. Below is an example: a new version of the code from a previous post.

    #target indesign
     
    var file = new File('~/Desktop/toysForUs.pdf');
     
    if (!file.exists) exit();
     
    var customObject = {}
    customObject.file = file;
    customObject.cropPage = 'CropToType.MEDIABOX';
    customObject.resolution = 300;
    customObject.usePageNumber = true;
    customObject.regExp = '\\.pdf$';
    var serializedObject = customObject.toSource();
     
    CreateBridgeTalkMessage(serializedObject);
     
    function CreateBridgeTalkMessage(serializedObject) {
         var bt = new BridgeTalk();
         bt.target = "photoshop";
         var script = "ResaveInPS = " + ResaveInPS.toSource() + "\r";
         script += "ResaveInPS(" + serializedObject + ");";
         bt.body = script;
     
         bt.onError = function(errObj) {
              $.writeln("Error: " + errObj.body);
         }
     
         bt.send(100);
    }
     
    function ResaveInPS(serializedObject) {
         var reconstructedObject = eval(serializedObject);
         var regExp = new RegExp(reconstructedObject.regExp.replace(decodeURI("%5C%5C"), decodeURI("%5C")));
         
          if (reconstructedObject.file.name.match(regExp) != null) {
              $.writeln( "This is PDF" );
              var pdfOpenOptions = new PDFOpenOptions;
              pdfOpenOptions.cropPage = eval(reconstructedObject.cropPage);
              pdfOpenOptions.resolution = reconstructedObject.resolution;
              pdfOpenOptions.usePageNumber = eval(reconstructedObject.usePageNumber);
         }
         else {
              $.writeln( "This is not PDF" );
         }
                   
         app.displayDialogs = DialogModes.NO;
         var doc = app.open(reconstructedObject.file, pdfOpenOptions);
         app.displayDialogs = DialogModes.ALL;
    }
    

    This approach, in my opinion, has at least two advantages:

    • You can pass all parameters in a single custom object instead of using a dozens of arguments
    • You can pass DOM objects like files and folders directly. So this way you can avoid making some extra steps: e.g. get an image's file path in InDesign, pass it to Photoshop, then in Photoshop create the File object from this path.

    It is important to note that in the function to be send via BridgeTalk we should use eval() for enumerations, boolean values and may be some other stuff.

     
    |
    Mark as:
  • John Hawkinson
    5,572 posts
    Jun 25, 2009
    Currently Being Moderated
    Mar 27, 2011 4:57 AM   in reply to Kasyan Servetsky

    (I'm going to be bad and post this without testing stuff, becaue i'm not near InDesign right now...I know this is going to come back to haunt me...)

    You can pass all parameters in a single custom object instead of using a dozens of arguments

     

    This is a great point, and one people should make more use of regularly. Once a function gets more than 2 or 3 arguments, it's always better to use:

    function doSomething(obj) {
      $.writeln(["The",obj.speed,obj.color,obj.animal,"jumped",
        obj.preposition,"the lazy dog"].join(" ");
    }
     
    doSomething( { speed: "quick", color: "brown", animal: "fox",
      preposition: "over" });
    

     

    because you don't have to remember the arguments, you can reuse code more easily, people can read it and tell what is going on, and all that good stuff. You can also assign default values to properties that aren't passed in. It certainly doesn't just apply to BridgeTalk!

     

    You can pass DOM objects like files and folders directly. So this way you can avoid making some extra steps: e.g. get an image's file path in InDesign, pass it to Photoshop, then in Photoshop create the File object from this path.

       

      Well, I think it's important to realize we're not passing DOM objects directly. We're figuring out how to make them and making new ones.

      So when you pass a File object, you don't get the File object's current position pointer, or other things associated with it.

       

      In your example you use a quoted string that contains a literal backslash, '\\.pdf$'. Does that actually work this time? We know it turns into a string with a single backslash, but does the backslash not get doubled when it makes it to PhotoShop?

       

      It is important to note that in the function to be send via BridgeTalk we should use eval() for enumerations, boolean values and may be some other stuff.

       

      I have to say, use of "eval()" frightens me...normally speaking, it's a language feature that makes it really easy to write code that is insecure and malicious people can tamper with. That's not such a huge problem in ExtendScript, but habits die hard (and it's not a habit exclusive to JavaScript, or even born from it in my case. It's a bad idea in /bin/sh scripts, perl scripts, etc....). So I'm skeptical...

       

      Why do you need to eval boolean values?

      I assume you are quoting and then evaling enumerations because you can't rely on the enumeration existing in InDesign, so compilation would otherwise fail. But that's not true of boolean values...

       

      Incidently, I have some other general comments on your script:

       

      I would tend to write the object more compactly. Perhaps:

       

      var serializedObject= {
        file: file,
        cropPage: 'CropToType.MEDIABOX',
        ...
        regexp: '\\.pdf$'
      }.toSource();
      

       

      You could even put it directly in line where you assign bt.body -- then it's right at the function call, which is where it belongs, I think. Best to keep the arguments near the function invokation, right?

       

      Also, if a property in your object needs to be evaled (like cropPage), it might be a good idea to name it in a way that is distinctive. like maybe evalCropPage or something, so that errors are reduced,  since they're fundamentally a different type, with a very different calling convention.

       

      I find this really confusing:

       

      var script = "ResaveInPS = " + ResaveInPS.toSource() + "\r";
      script += "ResaveInPS(" + serializedObject + ");";
      bt.body = script;
      

       

      We write the name of the function 3 times; we use "\r" to seperate two statements and rely on JavaScript's automatic insertion of semicolons; and we use an extra variable.

       

      Why not just write:

      bt.body = ResaveInPS.toSource()+"("+serializedObject+");";
      

       

      doesn't that just work? and of course, we could also write:

       

      bt.body = ResaveInPS.toSource()+"("+
        { file: file, ...
          regexp: '\\.pdf$' }.toSource()+");";
      

       

      Isn't that a lot clearer? It also means we avoid making ResaveInPS a global variable in PhotoShop. That's usually a good thing...

       
      |
      Mark as:
    • John Hawkinson
      5,572 posts
      Jun 25, 2009
      Currently Being Moderated
      Mar 28, 2011 12:42 AM   in reply to John Hawkinson

      In your example you use a quoted string that contains a literal backslash, '\\.pdf$'. Does that actually work this time? We know it turns into a string with a single backslash, but does the backslash not get doubled when it makes it to PhotoShop?

      Oh, wow, I wasn't paying attention to what you were doing.

       

      Anyhow, here's how I would do it, actually tested. For the regexp, I write it in /literal/ form, because that's the most readible, then we take the .source property and call encodeURI() on it. Reverse inside the function. To please JSLint, a bunch of other little changes -- braces {} around ifs, vars only at the top of functions, semicolons, etc. I just changed reconstitutedObject to obj, so it's actually typable -- if you want the clarity, I'd use a coment.

       

      In retrospect, since your CreateBridgeTalkMessage() function doesn't know anything about the function it is calling other than its name and target, it might be better written as follows, and called as CreateBridgeTalkMessage("photoshop", ResaveInPS, {file: file, ... } ):

       

      function CreateBridgeTalkMessage(target, f, obj) {
        var bt = new BridgeTalk();
        bt.target = target;
        bt.body = f.toSource()+"("+obj.toSource()+");";
      ...
      

       

      I dunno. Microoptimizations

       

      #target indesign
       
      var file = new File('~/Desktop/toysForUs.pdf');
       
      if (!file.exists) { exit(); }
        
      CreateBridgeTalkMessage({
              file: file,
              cropPage: 'CropToType.MEDIABOX',
              resolution: 300,
              usePageNumber: true,
              regExp: encodeURI(/\.pdf$/.source)
          });
       
      function CreateBridgeTalkMessage(obj) {
           var bt = new BridgeTalk();
           bt.target = "photoshop";
           bt.body = ResaveInPS.toSource()+"("+obj.toSource()+");";
           bt.onError = function(errObj) {
                $.writeln("Error: " + errObj.body);
           };
           bt.send(100);
      }
       
      function ResaveInPS(serializedObject) {
           var
              obj = eval(serializedObject),
              regExp = new RegExp(decodeURI(obj.regExp)),
              pdfOpenOptions, doc;
              
            if (obj.file.name.match(regExp)) {
                $.writeln( "This is PDF" );
                pdfOpenOptions = new PDFOpenOptions();
                pdfOpenOptions.cropPage = eval(obj.cropPage);
                pdfOpenOptions.resolution = obj.resolution;
                pdfOpenOptions.usePageNumber = obj.usePageNumber;
           }
           else {
                $.writeln( "This is not PDF" );
           }
                     
           app.displayDialogs = DialogModes.NO;
           doc = app.open(obj.file, pdfOpenOptions);
           app.displayDialogs = DialogModes.ALL;
      }
      
       
      |
      Mark as:
    • Currently Being Moderated
      Mar 28, 2011 5:51 AM   in reply to John Hawkinson

      Hi John,

       

      First  of all thank you very much for sharing your knowledge. Your code is  exactly what I was striving for — it is so compact and elegant! In my  future scripts I am going to use it as a template and I am sure it will  come in handy for guys on the forum as well.

       

      When  I was playing with my last version of the script, I tried to pass the RegExp as an object but nothing came out of this — eval() returned a generic Object. So I gave up, and passed it as a string with double  backslashes and then replacing them with single backslash (using the  approach you suggested). But now you showed me how to do this with encodeURI() and decodeURI()!

       

      I just changed reconstitutedObject to obj, so it's actually typable -- if you want the clarity, I'd use a coment.

      In  my 'real' scripts I use short names obj, set, gSet (object, settings, global settings). Since I was posting this on the forum, I meant to make it more readable and easy to understand e.g. for newbies.

       

      I have to say, use of "eval()" frightens me...normally speaking, it's a language feature that makes it really easy to write code that is insecure and malicious people can tamper with. That's not such a huge problem in ExtendScript, but habits die hard (and it's not a habit exclusive to JavaScript, or even born from it in my case. It's a bad idea in /bin/sh scripts, perl scripts, etc....). So I'm skeptical...

      I am not a programmer by background so have no idea how I can do any harm by using eval() method. In my scripting practice I use it quite often and sometimes I just can't  manage without it.

       

      Why do you need to eval boolean values?

      I assume you are quoting and then evaling enumerations because you can't rely on the enumeration existing in InDesign, so compilation would otherwise fail. But that's not true of boolean values...    

      Now I see, it was my mistake. But I remember in one of my previous scripts (it was reading the data from excel spreadsheets keeping them in a custom object) booleans failed to work until I used eval().

       

      Thank you again.

       

      Regards,
      Kasyan

       
      |
      Mark as:
    • John Hawkinson
      5,572 posts
      Jun 25, 2009
      Currently Being Moderated
      Mar 28, 2011 6:32 AM   in reply to Kasyan Servetsky
      When  I was playing with my last version of the script, I tried to pass the RegExp as an object but nothing came out of this — eval() returned a generic Object.

       

      Yeah, it appears that it when you call /test/.toSource(), you get back "({})" which is an empty object. That definitely seems wrong, but I guess the result is you need special handling. I suppose we could override RegExp.prototype.toSource()...

       

      So I gave up, and passed it as a string with double  backslashes and then replacing them with single backslash (using the  approach you suggested). But now you showed me how to do this with encodeURI() and decodeURI()!

       

      Gotcha. Sorry for giving confusing advice! I was trying to come up with ways to make it work at all, which wasn't quite the same as ways to make it work elegantly or neatl...

       

      I am not a programmer by background so have no idea how I can do any harm by using eval() method. In my scripting practice I use it quite often and sometimes I just can't  manage without it.

       

      Right, sorry. The quick answer is in most computer programs, there is the potential for a malicous user to do something. This is especially true in web programming, where a malicious user might try to get a server to execute code that it should not -- perhaps to give access to something it should not. If the program runs eval() on some string that a malicious user might have provided or altered, then the program is now executing arbitrary statements supplie by the user.

       

      For instance, let's look at the Adobe forums web server software (Jive). Suppose if when you clicked the HTML link in the upper-right, it showed you some strings that it would eval() as part of processing your message (if there server were running JavaScript; of course it's actually running Java instead...). If you were able to change them, maybe you could call the function the gives you more points, and gain more points on the forum! I know Peter Spier is always trying (unsuccessfully) to figure out how many points get him a stuffed animal. It looks like you've figured that out (your avatar), so maybe you have found and exploited such a vulnerability.

       

      Anyhow, in InDesign scripting, this is not very likely. You don't usually have to worry about the possibility of a malicious user changing a value you might eval(). Still, becuse of the potential to DO ALMOST ANYTHING, most programmers avoid eval(). But I think it is mostly unavoidable here.

       

      But I remember in one of my previous scripts (it was reading the data from excel spreadsheets keeping them in a custom object) booleans failed to work until I used eval().

       

      Oh, this makes sense. Because there you had the string "true" rather than the value true, if you just read it from the spreadsheet.

      Here's an example where most programmers would do something like (value==="true") rather than (eval(value)) because who knows

      what somebody might put in the spreadsheet?

       
      |
      Mark as:
    • Currently Being Moderated
      Mar 28, 2011 7:41 AM   in reply to John Hawkinson

      Thank you for the detailed explanation, John.

      I suppose we could override RegExp.prototype.toSource()...

      Have I got you right here?

      RegExp.prototype.toSource = function() {
           return encodeURI( this.source );
      }
       
      var regExp1 = new RegExp( /\.pdf$/ );
      var str = regExp1.toSource();
      var regExp2 = new RegExp( decodeURI(str) );
      

       

      Kasyan

       
      |
      Mark as:
    • John Hawkinson
      5,572 posts
      Jun 25, 2009
      Currently Being Moderated
      Mar 28, 2011 11:57 AM   in reply to Kasyan Servetsky

      Err, actually, no, not at all. I guess I was talking about several problems at the same time.

       

      a .toSource() method is supposed to return a string that, when eval()d, produces something that's just like the original object.

       

      So when you call "chicken".toSource(), you get back (new String("chicken")).

       

      The RegExp.toSource() method that ships with InDesign CS5 is broken. for /chicken/gi it returns ({}).

       

      So instead you want something like this:

      RegExp.prototype.toSource = function () {
          var flags ="";
          if (this.global) { flags+='g'; }
          if (this.ignoreCase) { flags +='i'; }
          if (this.multiline) {flags +='m'; }
          return ('(new RegExp('+this.source.toSource()+
              ',"'+flags+'"))');
      };
      

       

      which for  /chicken/gi it returns (new RegExp((new String("chicken")),"gi")).

       

      Then it would be automatically called by Object.toSource(), so if you put a regular expression literal in an object and serialized it with .toSource(), it would work properly:

       

      $.writeln(o = {
              usePageNumber: true,
              regExp: /\.pdf$/ 
          }.toSource());
      

       

      returns ({usePageNumber:true, regExp:(new RegExp((new String("\\.pdf$")),""))})

      I wish we didn't need to .toSource() the this.source, but I think it is necessary to get double-backslashes where required.

       

      But this has nothing to do with the BridgeTalk backslash problem. In my sample code, we special-case the regular expression. Probably not a great answer, because what if there are strings inside the object that contain backslashes?

       

      But again, we could do a variant of what I suggested in post #27 above, and encode the whole thing and decode it in the function.

      So perhaps

       

        bt.body = f.toSource()+"('"+encodeURI(obj.toSource())+"');";


      and then inside the function:

              obj = eval(decodeURI(serializedObject)),

      That really doesn't seem so bad. Remembering that we're working around a BridgeTalk bug here...

       
      |
      Mark as:
    • Currently Being Moderated
      Mar 29, 2011 6:13 AM   in reply to John Hawkinson

      Now it's clear to me. Thank you, John.

       

      Kasyan

       
      |
      Mark as:

    More Like This

    • Retrieving data ...

    Bookmarked By (1)

    Answers + Points = Status

    • 10 points awarded for Correct Answers
    • 5 points awarded for Helpful Answers
    • 10,000+ points
    • 1,001-10,000 points
    • 501-1,000 points
    • 5-500 points