Stacking Order of pageItems in CS5

New Here ,
Dec 06, 2011 Dec 06, 2011

Copy link to clipboard

Copied

I have not been able to get the correct stacking order of pageItems within a layer/page (in CS5). This was simple to do in CS4 as something like this (CurrentPageItem = myPage.pageItems😉 would return pageItems in the actual stacking order in the document.

With CS5 all textframes come together and all rectangles come together, irrespective of them stacked in any order (TextFrame, Rectangle, TextFrame,.... would come out as TextFrame, TextFrame and Rectangle). Some solutions already in the forum point to changing to older version (6.0), but I don't want to do that.

Can there be a way to determine STACKING ORDER within the layer/page in CS5?

TOPICS
Scripting

Views

5.5K

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Dec 06, 2011 Dec 06, 2011

Copy link to clipboard

Copied

Check the index of the pageItems.

The index is the placement in the z order and does not necessarily match the position in the collection...

Harbs

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Dec 06, 2011 Dec 06, 2011

Copy link to clipboard

Copied

Hi Harbs,

Well, the index property did give me the stacking order of pageItems, just that it clubs the index values according to the type of pageItem. So again, I got index value "0" twice, one for the textframes and one for the "rectangle". Ideally, I expected if the textframe was on top of the rectangle(an image container), the rectangle would have the index "0" and the textframe to have index "1" (if the z-index theory holds for all pageItems). Is there any other property that holds the information of stacking order (across all types of pageItems)? The desktop version of CS5 has this property within the layer (one can stack pageItems within the layer). Is there something analogous in the layer object?      

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Dec 06, 2011 Dec 06, 2011

Copy link to clipboard

Copied

I'm not getting duplicate indexes.

Can you give me more details on what you did to get the duplicate indexes?

Harbs

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Dec 06, 2011 Dec 06, 2011

Copy link to clipboard

Copied

Hi Harbs,

Well I'm trying to get the position (x and y axes co-ordinates and the stacking order along the z-axis) of all pageItems and writing that to an xml (the x-y position comes out fine). To get the stacking order I use the statement:

        for(var i=0; i<PageItemCount;i++)

        {

           

               CurrentPageItem = myPage.pageItems;

             

                //some other operations and then....

              OutPutXMLText += "<TAGINDEX>"+CurrentPageItem.index+"</TAGINDEX>";

              

               //some more operations

         }

What I land up with is something like this:-

<TAGNAME>P3_Price</TAGNAME>

<TAGINDEX>0</TAGINDEX>

and

<TAGNAME>P3_Image</TAGNAME>

  <TAGINDEX>0</TAGINDEX>

-Sumit

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Dec 06, 2011 Dec 06, 2011

Copy link to clipboard

Copied

Let's keep it simple.

What does this give you?

alert(myPage.pageItems.everyItem().index);

Harbs

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Dec 07, 2011 Dec 07, 2011

Copy link to clipboard

Copied

Hi Harbs,

This (alert(myPage.pageItems.everyItem().index);) does give me unique indices. Only thing is how to loop through these array items? I tried this

var items = myPage.pageItems.everyItem();

           

            for(var ic=0;ic<PageItemCount - 1;ic++)

            {               

                app.consoleout("ID - "+ items[ic]);

            }  

but this only gives the error "Object does not support propoerty..."

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Dec 07, 2011 Dec 07, 2011

Copy link to clipboard

Copied

If you loop over items, be it a collection or an array, you should count with items.length, not PageItemCount, however that is defined.

You may find items = myPage.pageItems.everyItem().getElements(); is both faster and easier.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Dec 07, 2011 Dec 07, 2011

Copy link to clipboard

Copied

To further expand on what John said, you can grab two arrays, one of the page items and another of the indexes to minimize the InDesign interactions like this:

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

var indexes = myPage.pageItems.everyItem().index;

then you can loop through the page items:

for(var i=0;i<pis.length;i++){

  if(indexes == whatever){

   //do whatever

  }

}

Harbs

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Dec 07, 2011 Dec 07, 2011

Copy link to clipboard

Copied

Hi Harbs,

Thanks for the help!! The key for me was alert(myPage.pageItems.everyItem().index). The items = myPage.pageItems.everyItem().getElements(); again took me to the old issue. What I'm doing now is getting the string output of  (myPage.pageItems.everyItem().index) and (myPage.pageItems.everyItem().id) and using that in an array to get the correct stacking order.

Thanks again!!!

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Dec 07, 2011 Dec 07, 2011

Copy link to clipboard

Copied

(A little off topic...)

John said:

If you loop over items, be it a collection or an array, you should count with items.length, not PageItemCount, however that is defined.

Why?

Lately I've taken to writing loops like this:

for(var i = 0, l = doc.pageItems.length; i < l; i++) {

    doc.pageItems...

}

Not accessing the length property on every iteration improves performance noticeably. On the document I just tested this with, which had only 144 page items, it took ~0.4 seconds off the loop.

Jeff

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Dec 07, 2011 Dec 07, 2011

Copy link to clipboard

Copied

Jeff:

If you loop over items, be it a collection or an array, you should count with items.length, not PageItemCount, however that is defined.

Why?

Lately I've taken to writing loops like this:

for(var i = 0, l = doc.pageItems.length; i < l; i++) {

I didn't mean you couldn't or shouldn't cache the length property (though doing so where it's unimportant does make scripts needlessly harder to read, as does using the comma operator inside the for loop). I merely meant that you shouldn't be obtaining the length some other way (perhaps from some other time where it no longer matches up), and then using it later on.

This is especially true when your script doesn't work and you don't post how your limits are being derived in your posted code...

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Dec 16, 2011 Dec 16, 2011

Copy link to clipboard

Copied

Hi, I'm new to this thread but used information from it recently to improve the behavior of some code that needs to process pageitems in back-to-front order.

The changes I made -- using the 'index' property -- are like this, assuming oPage is a Page object:

   var pageItemArray = oPage.pageItems.everyItem().getElements();     // get the collection's pageitems in an array

   var indexes = oPage.pageItems.everyItem().index;          // get the index values of each item too

   for( var iPageItem=pageItemArray.length-1; iPageItem > -1; iPageItem-- ) {

      var oPageItem = pageItemArray[ indexes[iPageItem] ];     // use index[iPageItem] instead of iPageItem to find the next pageitem

      ...

This seemed to work, but I'm processing an InDesign file today with a three-page spread -- a back cover, a spine, and a front cover of a book.  The back cover has 2 pageitems and the front has 5 items (one of which overlaps the spine page; the spine page itself has no items).

When my loop has 'oPage' looking at the back cover, the 'indexes' array is [5,3], and pageItemArray is [  [object Rectangle],[object Rectangle] ]

So now I'm confused.  The documentation says a pageitem's 'index' property is "The index of the PageItem within its containing object." -- but oPage only has two objects.  The code blows up if I run it with this set of data, because  pageItemArray[ indexes[0] ] translates to pageItemArray[5].

I wondered whether maybe the contents of the 'indexes' array is spread-relative, since 5 and 3 would work as indexes into oPage.parent.pageItems.everyItem().getElements().  Yet when my page loop comes around to the 'front' page,the 'indexes' array now contains [5,3,2,1,0].  I did not expect to see duplicates here, if these index values are spread-relative.  So that theory seems like it must be bogus.

Harbs or somebody, can you explain what I'm doing wrong here?  When I'm getting the 'indexes' array from oPage.pageItems.everyItem().index, shouldn't (according to the documentation) the values be the index of each of the page's items within that page?  I need code that will let me list the pageitems for each page of a spread, in bottom-to-top z-order for each page.  Do I need to just forget about using this approach altogether, and use each item's parentPage property to find which page it's on, and pre-process a spread's items into separate page-specific lists, then go through each list I've created?

Thanks for any help.

- Rich

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Dec 16, 2011 Dec 16, 2011

Copy link to clipboard

Copied

   var indexes = oPage.pageItems.everyItem().index;          // get the index values of each item too

Try

   var indexes = oPage.pageItems.everyItem().index.getElements();

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Dec 17, 2011 Dec 17, 2011

Copy link to clipboard

Copied

Thanks John,

Here’s what I found: Modifying my ‘var indexes = ...’ line as you suggest,

var indexes = oPage.pageItems.everyItem().index.getElements();

results in an exception:

oPage.pageItems.everyItem().index.getElements is not a function

...I guess since everyItem().index is an array of integers, whereas everyItem().getElements() returns an array of PageItem objects, each of which have the getElements() function defined.

- Rich

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Dec 17, 2011 Dec 17, 2011

Copy link to clipboard

Copied

Hmm, yeah, sorry, that was bad advice. Oops.

I'm just going to say I was too tired at 2am.

Now I'm just confused.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Dec 17, 2011 Dec 17, 2011

Copy link to clipboard

Copied

Welcome to the club!

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Dec 17, 2011 Dec 17, 2011

Copy link to clipboard

Copied

You can't use the index to specify the items within your array (like you discovered). The indexes specify the stacking order within the spread.

Instead of trying to explain how to use the indexes, it was easier to just write a function which does it...

function getPageItemsInZOrder(container){

    var objs = [], i;

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

    var indexes = container.pageItems.everyItem().index;

    for(i=0;i<pis.length;i++){

        objs.push({

            obj:pis,

            index:indexes

        })

    }

    objs.sort(sortByIndex);

    var retVal = [];

    for(i=0;i<objs.length;i++){

        retVal.push(objs.obj);

    }

    return retVal;

    function sortByIndex(a,b){

        return a.index-b.index;

    }

}

To use the function, you'd do something like this:

var myPageItems = getPageItemsInZOrder(myPage);

Harbs

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Dec 17, 2011 Dec 17, 2011

Copy link to clipboard

Copied

Thank you Harbs – your approach appears (after limited testing) to work great! But I can’t get by without at least understanding a little more about what’s going on here – you say “The indexes specify the stacking order within the spread.” – Why would two of the pages have index arrays of and ? Your code looks like it just uses a page’s index array as relative stacking order numbers; seemingly that’s all they’re good for, right? Do they correspond to any other data available for the spread or pages, like one of the allPageItems arrays’ entries or something like that?

But again, thanks – I appreciate not just an answer but a solution!

- Rich

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Dec 17, 2011 Dec 17, 2011

Copy link to clipboard

Copied

Hi Rich,

Harbs gave you the whole answer: "The [PageItem] indexes specify the stacking order within the spread."

To expand further on that, it is worth noting that, since CS5, "page items" are nothing but "spread items". The actual parent container of a top-level PageItem is always a Spread object, and Page objects are just artefacts. In CS4, there were Page-parented and Spread-parented items depending on the location of the object. The architecture has been radically changed in CS5 (which by the way added the parentPage property to maintain a kind of connection between items and pages, but this connection is virtual). Interestingly, pages themselves can easily behave as PageItem entities. The Page object now support transform(), it can be scaled/rotated/skewed in its own coordinate space relative to the Spread coordinate space or any other. Another fact is that you can transform a page independently of what we see as its page items, as shown here: http://www.indiscripts.com/post/2011/12/total-rescale-last-minute-layout-adjustment

Henceforth, I suggest we think pages just as simple spread items.

@+

Marc

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Dec 17, 2011 Dec 17, 2011

Copy link to clipboard

Copied

Marc Autret wrote:

The architecture has been radically changed in CS5...

Interestingly, the architecture has not really changed. Under the hood, Pages were always nothing more than rectangles.

In fact, the reason they changed the parent attribute was because they discovered that the single most processor intensive attribute to resolve on a page item was "parent" because resolving the page is not straight-forward. The natural parent of a page item under the hood is (and always has been) a spread.

Making the page the parent in scripting was nothing more than smoke and mirrors...

Harbs

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Dec 17, 2011 Dec 17, 2011

Copy link to clipboard

Copied

Enlightening!

Thanks.

Marc

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Dec 17, 2011 Dec 17, 2011

Copy link to clipboard

Copied

rgartlan wrote:

Your code looks like it just uses a page’s index array as relative stacking order numbers; seemingly that’s all they’re good for, right?

I'm not sure what you mean to ask.

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

Gives you an array of the page items contained by the object provided (i.e. a page)

var indexes = container.pageItems.everyItem().index;

Gives you an array of the indexes of each of those items in the same order.

So "pis.index" should be the same as "indexes".

sumitkdev seemed to indicate that pis.index gives different results, but I have not checked up on that. It would be surprising to me if that was true.

The index property is the stacking order of the object in a spread, so yes, that's all they are good for...

Harbs

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Dec 17, 2011 Dec 17, 2011

Copy link to clipboard

Copied

I still can’t figure out why for two pages in a spread, one index array would contain -- while the other contains . If these numbers indicate the relative stacking order within the spread for each page’s items, then what do we make of 5 and 3 being in both lists? Wouldn’t they necessarily refer to the same objects, as if both pages claimed ownership of these two items? There’s 7 total items on the spread – so what happened to 4 and 6, pray tell?

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Dec 19, 2011 Dec 19, 2011

Copy link to clipboard

Copied

Hi Rich,

The fact is that you are right and we are—partially—wrong. More precisely, the answer is incomplete in that it does not account for another issue: PageItems is a meta-collection which, in fact, target specific sub-collections (Rectangles, Ovals, TextFrames, etc.). Browsing within a PageItems collection by indexes may lead to something very tricky when different kind of underlying objects are involved.

Let's study the following layout:

PageItemOrder.png

This is a single-page document having three simple top-level items: a Rectangle, a TextFrame, and an Oval.

Considering the myDoc.PageItems collection, I don't know exactly how the internal indices are managed in that collection, but as it has been said a PageItem.index property returns a z-order, we can check this:

var doc = app.activeDocument;

var pgItems = doc.pageItems; // a PageItems collection

alert( pgItems.everyItem().index );

// => 0,2,1  (depending on the z-order)

// These are indices within the spread.PageItems collection

The resulting order, [0,2,1], indicates that:

  • pgItems[0] has the z-level 0 (front),
  • pgItems[1] has the z-level 2 (back)
  • pgItems[2] has the z-level 1 (middle)

From that we can infer that pgItems[0] refers to the Oval, pgItems[1] refers to the Rectangle, and pgItems[2] refers to the TextFrame.

But, what is really misleading is the following test:

alert([

    pgItems[0].index,

    pgItems[1].index,

    pgItems[2].index

    ]);

// => 0,0,0 !!

As you can see, pgItems[ i ].index returns 0 (zero) for each item, whereas we just have seen that pgItems.everyItem().index returns [0,2,1]. How is it possible?

In fact, the weird [0,0,0] result reflects indexes within the respective Rectangles, Ovals, TextFrames collections (each has a single element).

In other words, pgItems[ i ].index is actually resolved as pgItems[ i ].getElements()[0].index. This makes it practically very difficult to keep a relevant connection between the everyItem().index Array and the actual z-order of the page items. I think this is the reason for the issue you mention.

So, how to do? As a general rule, never rely on collection indices to identify an element. The only way to unambiguously and unvariantly refer to an object is to use the id property. If you need to deal with z-orders, backup the information in a id-to-zorder structure. Here is an approach:

var zOrderById = {},

    itemZO = pgItems.everyItem().index,

    itemIds = pgItems.everyItem().id,

    i = pgItems.length;

while( i-- )

    {

    zOrderById[itemIds] = itemZO;

    }

Then you can use zOrderById[ pgItems[ i ].id ] to retrieve the z-order of the i-indexed item in the pgItems collection:

alert( zOrderById[ pgItems[0].id ] ); // => 0

alert( zOrderById[ pgItems[1].id ] ); // => 2

alert( zOrderById[ pgItems[2].id ] ); // => 1

Of course, given a page item, myItem, you also can directly use: zOrderById[myItem.id].

@+

Marc

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines