16 Replies Latest reply on Apr 13, 2011 9:19 AM by Tom Tomasko

    Can this script run faster?

    Tom Tomasko Level 1

      Hello,

       

      The User dictionary does not always seem to work properly. The workaround is a script that creates discretionary hyphens for certain words.

       

      The following script first deletes all discretionary hyphens in a document being converted from PageMaker to InDesign. It then searches the document for 1900+ words and inserts discretionary hyphens in those words. The script has two arrays with that many elements, one for the words to be searched for, the other with the discretionary hyphens for those words. The script works fine but it takes about a minute to cycle through a 100 page document and 1900+ words.

       

      Is there a way to re-write this script so that it goes faster? Just curious.

       

      Thanks,

      Tom

       


      #target InDesign
      app.scriptPreferences.version = 5.0;
      var myDoc = app.activeDocument;
      var discrecHyphen = theGrepChanger(myDoc,"~-","");    

       

      if(discrecHyphen.length > 0){
           alert("I just deleted "+discrecHyphen.length+" discretionary hyphens.");
           }//end if
      else{
           alert("There were no discretionary hyphens in this document.");
           }//end else

       

      var countWords = 0;
      var numWords = "";
      var wordsChanged = [];
      var arrRawWords = ["humongous","array","of","thousand-plus","elements"];
      var arrHyphenWords = ["hu~-mon~-gous","ar~-ray","of","thousand-~-plus","el~-e~-ments"];
      for(var i =0; arrRawWords.length > i;i++){
           var numWords = theGrepChanger(myDoc,arrRawWords[i],arrHyphenWords[i]);
                if(numWords.length!=0){
                     wordsChanged.push(arrRawWords[i]);
                     }//end if
                countWords +=numWords.length;    
           }//end for i

       

      alert ("I just added discretionary hyphens for " +countWords+" words.\r\rSweet, huh?!");

       


      //*****functions*******
      function theGrepChanger(docRef,grepFindIt,grepChangeIt){
           app.findGrepPreferences = NothingEnum.NOTHING;
           app.changeGrepPreferences = NothingEnum.NOTHING;
           app.findGrepPreferences.findWhat = grepFindIt;
           app.changeGrepPreferences.changeTo = grepChangeIt;
           var arrGrepFindIt = myDoc.changeGrep();
           return arrGrepFindIt;
      }//end theGrepChanger

        • 1. Re: Can this script run faster?
          Peter Kahrel Adobe Community Professional & MVP

          A minute to change 1900+ words in a 100-page document sounds pretty good to me. The only room for some tuning seems to be your theGrepChanger function. There's no need to set the find and changeGrepPreferences to null at every iteration of the loop (i.e. 1900+ times) Remove these two lines from the function and place them at the beginning of the script:

           

               app.findGrepPreferences = NothingEnum.NOTHING;
               app.changeGrepPreferences = NothingEnum.NOTHING;

           

          And maybe you could return the change-array's length, rather than the array itself:

           

          return arrGrepFindIt.length;

           

          but I don't know id that makes any difference.

           

          Peter

          • 2. Re: Can this script run faster?
            Harbs. Level 6

            Doing 2000 GREP find/changes on a doc is going to take time.

             

            Probably the only thing you can do is to trim down the number of find/changes you are going to do. Native JS is much faster. (BTW, You don't want to do contents.replace() because it will kill your doc.)

             

            You can grab the contents of your doc, and go through your list and use contents.match() to find the ones which are actually used. Then go through your doc and do the GREP find/changes using the trimmed down list...

             

            HTH,

            Harbs

            • 3. Re: Can this script run faster?
              Fred Goldman Level 3

              If you are working in CS4 or CS5 making the text overset makes the find/changes considerably quicker.

               

              You probably are not making too many changes with those 1900+ find/changes or it would take much longer.

              • 4. Re: Can this script run faster?
                John Hawkinson Level 5

                To make it faster, you have to understand where it's slow. If, as has been theorized, the .changeGrep() is the bottleneck, there's not a lot you can do.

                How many stories do you have?

                 

                One approach would be export the stories to InDesign Tagged Text, do the replacements there as pure text operations, and then re-place the IDTT files. This gets complicated quickly.

                 

                Similarly complicated would be to export the whole doc to IDML, do the replacements on the IDML, and then open the IDML in InDesign.

                 

                One observation is that in Javascript, unlike most languages, arrays are frightfully slow. In most implementations they are a linear search each time. Objects (hashes) are much  faster.

                 

                So you might be better off with:

                 

                var rawWords = { humongous: "hu~-mon~-gous", array: "ar~-ray" };
                for (i in rawWords) {
                       var numWords = theGrepChanger(myDoc,i, rawWords[i]);
                

                 

                But only if the array searching is significant time. And probably it's the grep.

                 

                I guess the ESTK has a profiling tool, I've never needed to use it, but it could be handy here. And you can mess with Date().valueOf() and subtraction to time things.

                 

                p.s.: pasting your code with Java syntax highlighting makes it much easier to read.

                • 5. Re: Can this script run faster?
                  John Hawkinson Level 5

                  OK, it's not the arrays. If you turn on the ESTK's profiler, you get this data:


                  LineTimeHits
                  111
                  461
                  519391
                  61831
                  751
                  1611
                  1711
                  18141
                  373897851901
                  3819810851901
                  3926927411901
                  4011977581901
                  4111323691901
                  42389690871901
                  4321651901
                  4417411901
                  4592

                   

                  I wrapped your script in a function() {} to see if it would report times in the array lookups, so it doesn't. Perhaps it's being optimized out. Anyhow, it spends all the time in line 42, which is the mydoc.changeGrep(). Oh, the document -- I placed Alice in Wonderland in 12pt Minion Pro Regular on 5.5"x8.5" pages, and then used as my raw words the first 1900 words in /usr/share/dict/words and as their hyphenation pairs inserted a ~ after every letter 'c'. Takes about 46 seconds to run.

                   

                  You might also think it'd run faster if you opened the document with app.open(File("alice.indd",false) so it shows no window. This seems sort of true, but then ID crashes on the 872nd word ("accept", hyphed as "ac~c~ept"), probably one of the few words in my hyphenation list that actually shows up in Alice. Oh well... It also doesn't seem much faster -- takes about 20 seconds to get to 872, which seems about the same time as with the window open.

                  • 6. Re: Can this script run faster?
                    Peter Kahrel Adobe Community Professional & MVP

                    > and go through your list and use contents.match() to find the ones which are actually used

                     

                    I tried that a while ago but InDesign's Grep turned out to be much quicker than JS's on a big text string.

                     

                    Fred -- Interesting idea, didn't know that. But since Tom's script is already pretty quick, the overhead over getting the text overset and back to non-overset probably makes the script run longer.

                     

                    Peter

                    • 7. Re: Can this script run faster?
                      absqua Level 4

                      I too found Fred's idea of operating on overset text very interesting. I assumed it must be quicker because it didn't have to recompose the story after each change.

                       

                      I have a number of iterative copyfitting routines which work by modifying pointSize, spaceAfter, etc. until the text is no longer overflowed. Even though they don't involve changeGrep or changeText, I thought they might be helped by Fred's suggestion. I found, however, that the small gains I made modifying the text attributes while overset were wiped out (and then some) by the cost of shrinking/fitting the frame, just as Peter thought. Too bad!

                       

                      I wish the recompose() method had an opposite, like pauseComposition(), that you could call before looping through all the textStyleRanges in a chunk of text.

                       

                      Jeff

                      • 8. Re: Can this script run faster?
                        Harbs. Level 6

                        absqua wrote:

                         

                        I wish the recompose() method had an opposite, like pauseComposition(), that you could call before looping through all the textStyleRanges in a chunk of text.

                         

                        Yeah. I've been asking for this for years...

                         

                        Harbs

                        • 9. Re: Can this script run faster?
                          absqua Level 4

                          I've wondered whether it might be possible to write a plug-in whose only job was to expose to the scripting framework such a method (and a resumeComposition() as well, I guess). But I don't have enough understanding of the API to get beyond idle wondering...

                          • 10. Re: Can this script run faster?
                            Harbs. Level 6

                            No. I don't think that the method exists on the C++ level either...

                             

                            Enabling the ability to freeze composition is not a small task. Unfortunately, I don't think that's going to happen anytime soon. (I hope I'm wrong!)

                             

                            Harbs

                            • 11. Re: Can this script run faster?
                              John Hawkinson Level 5

                              Where this matters, isn't it sufficient to close the document and reopen it without showing a window?

                              • 12. Re: Can this script run faster?
                                Harbs. Level 6

                                Nope.

                                 

                                Opening without a Window only helps for screen redraw issues. One of the biggest performance issues with scripts is when your script causes InDesign to recompose the text 1000 times. Text composition has nothing to do with screen redraw. Sometimes you need InDesign to recompose text so you can know where text breaks, but a lot of times it's not necessary. The composer is not really supposed to kick in until you ask for a property which requires composition, but it doesn't really work like it should...

                                 

                                This problem is not limited to scripts. Plugins can have the same issues, but depending on which APIs your plugin uses, you might have more control.

                                 

                                Maybe I'll chat with Douglas about this in D.C. during the PeP Conference...

                                 

                                Harbs

                                • 13. Re: Can this script run faster?
                                  Fred Goldman Level 3

                                  That would be awesome! Please let us know what he says.

                                  • 14. Re: Can this script run faster?
                                    [Jongware] Most Valuable Participant

                                    Would ID play nice if you set the entirety of the text to No Break before fiddling with it, and defer recomposition until you "released" it again?

                                     

                                    Mind you, if so, it'd definitely be one for the Dirty Tricks Book, since it would destroy existing manually applied No Breaks.

                                    • 15. Re: Can this script run faster?
                                      John Hawkinson Level 5

                                      It'd probably be sufficient to insert a single line that was NO

                                      BREAKed that was wider than your widest column. Then you could easily

                                      remove it without affecting the NO BREAK status of the rest of the

                                      story.

                                      • 16. Re: Can this script run faster?
                                        Tom Tomasko Level 1

                                        Thanks everyone for your input. I tried Peter's suggestion to take out of the function setting the Grep preferences to Nothing.

                                         

                                        In an 85 page document that had discretionary hyphens added to 2068 words, it saved me a grand total of 3.758 seconds. The total time was 60.468 seconds using Peter's suggestion. Leaving the Grep function as is took 64.216 seconds.

                                         

                                        If the script were to be used in a mass production setting I would probably rewrite it. But taking a minute  and four seconds more is OK for the way it is being used.

                                         

                                         

                                        Tom