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?
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
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?
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
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
Copy link to clipboard
Copied
Let's keep it simple.
What does this give you?
alert(myPage.pageItems.everyItem().index);
Harbs
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..."
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.
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
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!!!
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
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...
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
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();
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
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.
Copy link to clipboard
Copied
Welcome to the club!
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
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
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
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
Copy link to clipboard
Copied
Enlightening!
Thanks.
Marc
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
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?
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:
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:
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