24 Replies Latest reply on Aug 4, 2017 1:22 AM by samia84304672

    [JS] :: [CSX] - PageItem Stacking Order

      Hi folks,

      Does any one here have any knowledge and or experience on how indesign handles the stacking order of page items.

      I have a simple script which can be viewed here:
      http://home.exetel.com.au/thunder/img/simpleScript.js

      The indesign file I am opening can be downloaded here:
      http://home.exetel.com.au/thunder/img/sample%20Folder.zip

      I am trying to use the index property of pageItem to determine the proper stacking order--I am trying to sort the page Items from top down.

      In This example inDesign reports that the object 'little circle' is underneath 'big Circle' and the index numbers in general do not seem to match how I have placed the objects on top of each other in the document.

      Is this the correct way to determine stacking order?
        • 1. Re: [JS] :: [CSX] - PageItem Stacking Order
          Peter Kahrel Adobe Community Professional & MVP
          Indexes indicate the stacking order of objects, but only of objects of the same type. You have two types of object on your page, three Ovals and two Rectangles. The Ovals have indexes 0, 1, and 2, the Rectangles, 0 and 1. On pages with objects of mixed types it is therefore not possible (or not straightforward anyway) to determine those objects' stacking order. (An object's id doesn't change when you send it to the front or back, so that doesn't tell you anything about stacking order.)

          Peter
          • 2. Re: [JS] :: [CSX] - PageItem Stacking Order
            Level 1
            Thank you for your reply Peter,

            Your message helped me understand how indesign assigns indicies to PageItems and why I cant use them to determine a stacking order. (Unless they are all the same type of object). Can you think of any other ways this might be done or any scripts out there that are already doing this? I thought if I could add all of my page items to groups and therefore bring them all into the same type this would produce an accurate stacking order. But the groups I make do not stay in the same order and find myself back to square one.

            Thanks again for your reply.
            Ben
            • 3. Re: [JS] :: [CSX] - PageItem Stacking Order
              Level 1
              I had another go with my grouping strategy and came up with a solution that works. I have uploaded a new modified version of my script here:
              http://home.exetel.com.au/thunder/img/simpleScript2.js

              Basically I create a bunch of empty text frames and group them to all of the individual page items, this creates a new set of page items who are all of the same type 'Group' and therefore share unique indicies which represent their stacking order.

              This script could surely by improved and optimized. This method seems entirely over the top for something that should be very simple. I am open to better suggestions.
              • 4. Re: [JS] :: [CSX] - PageItem Stacking Order
                Peter Kahrel Adobe Community Professional & MVP
                Ben,

                What a great idea: duplicate pageItems and group the original and the duplicate so that all page items are of the same type. But you're making things very complicated. Your brilliant idea can be scripted simply as follows: on each page, duplicate each page item and group it with its original. Label the duplicates so that you can delete them easily later on:
                doc = app.activeDocument;
                
                for (i = 0; i < doc.pages.length; i++)
                   {
                   p_items = doc.pages[i].allPageItems;
                   for (j = 0; j < p_items.length; j++)
                      {
                      dupl = p_items[j].duplicate();
                      dupl.label = 'duplicate';
                      doc.pages[i].groups.add ([dupl, p_items[j]]);
                      }
                   }

                Afterwards these two lines ungroup everything and delete the duplicates:
                doc.groups.everyItem().ungroup();
                
                doc.pageItems.item ('duplicate').remove()


                Peter
                • 5. Re: [JS] :: [CSX] - PageItem Stacking Order
                  Level 1
                  Peter,

                  unless you don't have other groups you don't want to ungroup ;)
                  I think all new temp groups should be labeled and only these groups should be ungrouped

                  robin

                  --
                  www.adobescripts.com
                  • 6. Re: [JS] :: [CSX] - PageItem Stacking Order
                    Peter Kahrel Adobe Community Professional & MVP
                    >I think all new temp groups should be labeled and only these groups should be ungrouped

                    Good point. The line that creates the groups should then be
                    >doc.pages[i].groups.add ([dupl, p_items[j]], {label: 'dupl_group'});

                    and the line that ungroups should then be
                    >doc.groups.item('dupl_group').ungroup();

                    Peter
                    • 7. Re: [JS] :: [CSX] - PageItem Stacking Order
                      Level 1
                      in VBS - after grouping selected items - order is from top to bottom
                      so - I think there is no need to play with duplicates ... just group, read order of elements in group and ungroup
                      of course - this is OK ONLY for items from same layer
                      in case of few layers - this need to be done for each layer

                      robin

                      --
                      www.adobescripts.com
                      • 8. Re: [JS] :: [CSX] - PageItem Stacking Order
                        Peter Kahrel Adobe Community Professional & MVP
                        Robin,

                        >in VBS - after grouping selected items - order is from top to bottom

                        That's the case in JS, too. But the moment you ungroup, each object type gets its own range of indexes again. Maybe Ben can do what he wants to do while the objects are grouped.

                        You're right that layers are another consideration. Anyway, all I wanted to point out to Ben was that what he tried to achieve needn't be that complicated.

                        Peter
                        • 9. Re: [JS] :: [CSX] - PageItem Stacking Order
                          Level 1
                          Peter,

                          I know that your solution is faster and simpler ;)
                          and I know that all objects back to ID-order after ungrouping - but when objects are temp group - order of IDs could be stored in array for later use

                          robin

                          --
                          www.adobescripts.com
                          • 10. Re: [JS] :: [CSX] - PageItem Stacking Order
                            Level 1
                            Wow thanks Peter for your optimized/improved script that makes things a lot more elegant and efficient. Thank you Robin for your insight also. In my project I can work with the page Items while they are grouped and ungroup them all at the end. Different layer constructs do not concern me for this project only different pages.

                            Cheers
                            Ben
                            • 11. Re: [JS] :: [CSX] - PageItem Stacking Order
                              Harbs. Level 6
                              Hi guys,

                              Unless I'm missing something obvious, this technique should work for getting the stacking order of objects (without duplicating, etc.):

                              1)
                              var items = page.pageItems.everyItem().getElements().slice(0)
                              
                              (to get an array of all top level page items.)
                              2) for each item you'd find the index in the items array. Here's a function I use:

                              function GetItemIndex(array,item){
                                for(var i=0;i<array.length;i++){
                                  if(array[i] == item){return i}
                                  }
                                return null;
                                }

                              3) then you just compare the indexes.

                              Harbs
                              • 12. Re: [JS] :: [CSX] - PageItem Stacking Order
                                Peter Kahrel Adobe Community Professional & MVP
                                Hi Harbs,

                                The rest of us were missing something obvious! You make it look so easy :)
                                But as a matter of interest, what's the ".slice(0)" doing there at the end of the first line?

                                Peter
                                • 13. Re: [JS] :: [CSX] - PageItem Stacking Order
                                  Level 1
                                  Oh I get it--the pageItems Array is already in the right order and we should use the array's index not the index property ...

                                  Nice one Harbs!
                                  • 14. Re: [JS] :: [CSX] - PageItem Stacking Order
                                    Level 1
                                    Harbs,

                                    the same way we can check order of selected objects in older versions of ID - but this was removed in CS3 and this misled me ;)

                                    robin

                                    --
                                    www.adobescripts.com
                                    • 15. Re: [JS] :: [CSX] - PageItem Stacking Order
                                      Harbs. Level 6
                                      Peter Kahrel wrote:
                                      > But as a matter of interest, what's the ".slice(0)" doing there at the end of the first line?
                                      >

                                      I'm not sure if it really makes a difference in this case (I've never
                                      bothered to check), but here's an article written by Kris Coppieters
                                      which explains the idea behind it...
                                      http://www.niemannross.com/developer/wiki/index.php?title=Avoiding_slowness_with_object_ar rays

                                      and here's one on collections:
                                      http://www.niemannross.com/developer/wiki/index.php?title=Converting_a_collection_into_an_ array

                                      FWIW, I use this function to get an array when I deal with a large
                                      number of objects and I don't specifically need a dynamic collection
                                      over an array. I don't need to worry about whether I'm starting out with
                                      an array or a collection and the difference in performance is tremendous:

                                      function AsArray(collection){
                                      if(collection instanceof Array){return collection.slice(0)}
                                      return collection.everyItem().getElements().slice(0);
                                      }

                                      --
                                      Harbs
                                      http://www.in-tools.com
                                      • 16. Re: [JS] :: [CSX] - PageItem Stacking Order
                                        Harbs. Level 6
                                        Peter,

                                        Your question finally prompted me to do some timing tests:

                                        I created 400 text frames for testing purposes.

                                        Accessing pageItems to check if they were text frames took about 20
                                        times as long when they were collections than with
                                        everyItem().getElements(). adding the "slice(0)" cut the time by about a
                                        third. So slice(0) doesn't make a very big difference in this case, but
                                        it does make a difference...

                                        --
                                        Harbs
                                        http://www.in-tools.com
                                        • 17. Re: [JS] :: [CSX] - PageItem Stacking Order
                                          Peter Kahrel Adobe Community Professional & MVP
                                          Harbs.

                                          I knew that arrays process much quicker than collections, but didn't know that .slice(0) sped up things even more -- thanks very much for the pointer. But I can't reproduce that. I did a test similar to yours (a document with 400 pages, a text frame on each page, then tested if each object was a text frame), and it turned out that adding .everyItem().getElements() reduced the script's running time by a factor 20, which is what you found, too, but then adding .slice(0) didn't matter at all (see timings below). Maybe any fractional advantage in processing the array is cancelled by the overhead created by .slice(). So it looks as if Kris is not entirely correct when he says

                                          The '.slice(0)' is there to force a duplication of the array.
                                          
                                          If it were omitted, InDesign would merely copy a reference to
                                          theRealm.allPageItems, and we'd be no better off.


                                          Maybe the differences are due to the type of test (.slice(0) may have a distinct advantage on longer/shorter arrays) or type of computer (you can measure the difference, I can't). Interestingly, Kris adds another optimalisation by assigning the array's length to a variable:

                                          var realmItems = theRealm.allPageItems.slice(0);
                                          
                                          var realmLength = realmItems.length;
                                          for (var idx = 0; idx < realmLength; idx++)


                                          This has long been known to speed up array-processing, but again its effect turns out to depend on the type of test and computer. The speed tests show that collections benefit, but arrays don't always. On my PC, assigning array length to a variable and using that in the loop doesn't make any difference, but on an older computer that I used previously, which was much slower (0.8 vs 3.2 GHz), it did make a big difference: it halved an 'array-intensive' script's running time.

                                          Anyway, here are some timings. 400-page document, text frame on each page, document has no undo-stack. Timings are highest and lowest of ten runs. Time differences such as 0.14 vs. 0.12 can be considered meaningless.

                                          Peter

                                          3.6-3.7 (seconds)
                                          
                                          t = app.activeDocument.textFrames;
                                          for (i = 0; i < t.length; i++)
                                             if (t[i] instanceof TextFrame) {}

                                          0.06-0.11
                                          t = app.activeDocument.textFrames.everyItem().getElements();

                                          0.06-0.14
                                          t = app.activeDocument.textFrames.everyItem().getElements().slice(0);

                                          2.5-2.6
                                          t = app.activeDocument.textFrames;
                                          t_length = t.length;
                                          for (i = 0; i < t_length; i++)
                                             if (t[i] instanceof TextFrame) {}

                                          0.06-0.14
                                          t = app.activeDocument.textFrames.everyItem().getElements();

                                          0.06-0.12
                                          t = app.activeDocument.textFrames.everyItem().getElements().slice(0);


                                          PS: there have been claims that there might be a speed difference between 'x instance of y' and 'x.constructor.name == "y"', but that's not borne out by repeating the above tests using constructor.name.

                                          P.
                                          • 18. Re: [JS] :: [CSX] - PageItem Stacking Order
                                            Harbs. Level 6
                                            Hi Peter,

                                            Yes, you are correct. I was looking at the timing data in the ESTK, and
                                            I forgot to take the overhead of slice(0) into account.

                                            Without doing further testing, it looks like slice(0) doesn't hurt but
                                            it doesn't help either (for arrays created by getElements() ).

                                            --
                                            Harbs
                                            http://www.in-tools.com
                                            • 19. Re: [JS] :: [CSX] - PageItem Stacking Order
                                              Harbs. Level 6
                                              I just realized that I did the tests on CS4. I wonder if CS/CS2/CS3 will
                                              give different results...

                                              --
                                              Harbs
                                              http://www.in-tools.com
                                              • 20. Re: [JS] :: [CSX] - PageItem Stacking Order
                                                RorohikoKris-u5pUJw Level 2

                                                Further to the speeding up of ExtendScript  (see WissenWanne, http://niemannross.com/developer/wiki/index.php?title=JavaScript_tips_and_tricks ): I did my original tests against CS2 or CS3 - so some things might have since been optimized in CS4.

                                                 

                                                The added .slice(0) mentioned in previous posts might not be useful for collections in CS4, but I seem to remember it did make a difference in older versions of InDesign.

                                                 

                                                But the main reason I used .slice(0) was to remove any 'dynamism' from the returned array - i.e. completely detach the array of elements from the original collection.

                                                 

                                                Collections are 'live' objects that auto-update all the time to reflect the document structure. Proper primitive arrays are 'dead' - they don't change as a result of changes in the document.

                                                 

                                                Suppose I grabbed a collection of all items on a particular page, and then started moving these items one by one to other pages.

                                                 

                                                If I'd rely on the collection, that collection would auto-update as each item is being moved away. If I instead rely on a totally detached array, the array won't change even though elements are 'disappearing' from the collection that I used to initialize the array.

                                                 

                                                I forgot the exact details, but I remember that without the .slice(0) I still had some 'live link' between the apparent 'array' returned by getElements() and the collection. Using .slice(0) destroyed that (undesirable) live link. I did not really dig deep into that, and the point might have become moot in CS4.

                                                 

                                                But more importantly, I do remember that using .slice(0) was of great influence when applied to .allPageItems - which is not a collection, but something that pretends to be an array.

                                                 

                                                Using the .allPageItems.slice(0) made a tremendous difference with older versions of InDesign. Haven't re-tested with CS4 though.

                                                 

                                                Cheers,

                                                 

                                                Kris

                                                • 21. Re: [JS] :: [CSX] - PageItem Stacking Order
                                                  deepak.srinivasa

                                                  Thanks Harbs for your answer. I now know how to determine the stacking index of a pageitem.

                                                   

                                                  However, I am not able to figure out how to change the stacking order of a pageitem - like bring a pageitem to front and so on. I tried changing the order of elements in the pageitems array without much success (may be I'm not doing this right). Also, I see that only Rectangle class has methods like bringForward(), bringToFront() etc. I'm not sure how I can use methods of Rectangle, while I want to work at PageItem level.

                                                   

                                                  Any ideas?

                                                   

                                                  Thanks for your time,

                                                  Deepak.

                                                  • 22. Re: [JS] :: [CSX] - PageItem Stacking Order
                                                    Harbs. Level 6

                                                    PageItem is a base class. Any class that can reside on a page has a bringForward() method. (i.e. Rectangle, Group, Polygon, TextFrame, etc.)

                                                    • 23. Re: [JS] :: [CSX] - PageItem Stacking Order
                                                      Gaius Coffey Level 2

                                                      Hi,

                                                      What I'm seeing is almost the exact opposite of one of the critical assumptions here! 

                                                       

                                                      If I use this;

                                                      var pis = container.pageItems.everyItem().getElements ();    

                                                       

                                                      Based on what you were saying above, I should see an array with a mix of object types in the correct zOrder.

                                                       

                                                      What I am actually seeing is Rectangles, then Polygons, then TextFrames...

                                                       

                                                      This is based on InDesign CC, so I don't know whether something has changed?

                                                       

                                                      However, more pertinently, is there an efficient way for me to derive the .index value reliably? I am seeing times of 5 minutes+ to interrogate the .index value on a list of page items where a designer copy-pasted vector art as a background...

                                                       

                                                      Thanks,
                                                      G

                                                      • 24. Re: [JS] :: [CSX] - PageItem Stacking Order
                                                        samia84304672

                                                        "

                                                        var pis = container.pageItems.everyItem().getElements ();  

                                                         

                                                        Based on what you were saying above, I should see an array with a mix of object types in the correct zOrder.

                                                         

                                                        What I am actually seeing is Rectangles, then Polygons, then TextFrames...

                                                        "

                                                         

                                                        I have the same problem with Indesign CC (2017). Did you find a solution for this problem?

                                                         

                                                        EDIT: found solution in this comment: https://forums.adobe.com/message/4091126#4091126