• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

Iterate through Stories in page order

Community Beginner ,
Apr 18, 2024 Apr 18, 2024

Copy link to clipboard

Copied

I'm using var myStories = app.activeDocument.stories to get all the stories in a document. I want to loop through the stories, but in page order, which is not strictly how InDesign references them. Any advice on how to iterate through (or first *sort* before iterating through) myStories in page order? Thank you!
TOPICS
Scripting

Views

327

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

correct answers 1 Correct answer

Community Expert , Apr 18, 2024 Apr 18, 2024

Use getElements() to convert the collection into an array, then use this sort function, like so: 

var myStories = app.activeDocument.stories.everyItem().getElements();
myStories.sort(function(a,b) {
    return a.textContainers[0].parentPage.documentOffset < b.textContainers[0].parentPage.documentOffset;
});

Votes

Translate

Translate
Community Expert ,
Apr 18, 2024 Apr 18, 2024

Copy link to clipboard

Copied

Use getElements() to convert the collection into an array, then use this sort function, like so: 

var myStories = app.activeDocument.stories.everyItem().getElements();
myStories.sort(function(a,b) {
    return a.textContainers[0].parentPage.documentOffset < b.textContainers[0].parentPage.documentOffset;
});

Votes

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
Community Beginner ,
Apr 19, 2024 Apr 19, 2024

Copy link to clipboard

Copied

Thank you~  haven't had a chance to do much testing yet but that appears to do it. In limited testing, I changed to > rather than a < in the sort. Using <, the story on the last page came up first.

Votes

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 ,
Apr 19, 2024 Apr 19, 2024

Copy link to clipboard

Copied

@Andrew24943677v30i 

 

Without wanting to criticize my colleagues, I strongly discourage the proposed solution (which anyway seems problematic in other respects), simply because commands such that 

 

a.textContainers[0].parentPage.documentOffset

 

within a custom sort function will explode the Scripting DOM as soon as the document contains a huge number of stories. In ExtendScript, a custom sort involves on average 5×N×Log(N) internal calls of the function.

 

Note. - You should also take care of possible runtime errors (e.g. parentPage may be null, etc) and other issues (what about master pages?)

 

As usual, the solution I suggest is based on generating safe UTF16 sort keys through one linear loop, calling the native Array.prototype.sort() and building the desired array. Safer and probably much faster with long documents.

 

Here is a quick implementation [not tested in depth but you get the idea]

 

 

function sortedStories(/*Document*/doc,  K,ev,r,a,i,t)
{
  // Init.
  const CHR = String.fromCharCode;
  K=doc.stories, ev=K.everyItem(), r=ev.id, a=ev.textContainers;

  // Make sort keys  ->  r[i] :: <key> + storyId
  for
  (
    i=r.length ; i-- ;
    // ---
    (t=a[i]).length              // Have we a container?
    && (t=t[0].parentPage)           // Have we a parent page?
    && 'Spread'===t.parent.constructor.name  // Skip master pages!
    ? ( r[i]=CHR(1+t.documentOffset)+r[i] )  // Make 1st char sortable, add the ID.
    : r.splice(i,1)
  );

  // Instant sort.
  r.sort();

  // Build array of sorted stories and return.
  for( i=r.length ; i-- ; r[i]=K.itemByID(parseInt(r[i].slice(1),10)) );
  return r;
};

var myStories = sortedStories(app.activeDocument);
// etc

 

 

[EDIT] Still, this is certainly not the optimal way to reach the goal, but I wanted to illustrate my point while keeping the textContainers[0].parentPage.documentOffset approach proposed by @brianp311 

 

Best,

Marc

Votes

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
Community Expert ,
Apr 20, 2024 Apr 20, 2024

Copy link to clipboard

Copied

Hi @Marc Autret 

 

Appreciate the refined approach and additional insight. I was aware of issue related to null parentPages and stories in parent spreads; it was definitely a quick and dirty solution that could throw errors that the user then could explore how to work through as part of their learning journey. 

 

Really interesting approach with stringifying the sort values by offset / IDs and reconstructing the array. Thanks for your insights as always! 

Votes

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
Community Expert ,
Apr 18, 2024 Apr 18, 2024

Copy link to clipboard

Copied

Alternatively you can iterate the pages collection and each page has a textframes collection so you can get the story from the textframes.

-Manan

Votes

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
Community Expert ,
Apr 19, 2024 Apr 19, 2024

Copy link to clipboard

Copied

quote

Alternatively you can iterate the pages collection and each page has a textframes collection so you can get the story from the textframes.

-Manan


By @Manan Joshi

 

But OP wants order for the whole document - not for the current page?

 

Votes

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
Community Expert ,
Apr 20, 2024 Apr 20, 2024

Copy link to clipboard

Copied

Hi Robert, Manan was talking about iterating through the whole page collection. Probably is a faster approach than what I proposed in a longer doc too. 

Votes

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
Community Expert ,
Apr 20, 2024 Apr 20, 2024

Copy link to clipboard

Copied

@brianp311

 

But that's not what OP wants? 

 

OP wants order of the stories in the document by pages - not on a specific single page. 

 

Votes

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
Community Expert ,
Apr 21, 2024 Apr 21, 2024

Copy link to clipboard

Copied

I did not get you @Robert at ID-Tasker we would iterate story of each page is what I suggested.

-Manan

Votes

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
Community Expert ,
Apr 22, 2024 Apr 22, 2024

Copy link to clipboard

Copied

quote

I did not get you @Robert at ID-Tasker we would iterate story of each page is what I suggested.

-Manan


By @Manan Joshi

 

But Stories are per Document - not per Page - so you would be analysing the same Stories over and over again.

 

And OP wants order of the Stories in the Document - not on any particular Page. 

 

Votes

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
Community Expert ,
Apr 22, 2024 Apr 22, 2024

Copy link to clipboard

Copied

They want to iterate stories in page order. So if I iterate the pages and check the stories on it and ignore the story already encountered before like linked frames spanning pages then what would I be missing?

-Manan

Votes

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
Community Expert ,
Apr 23, 2024 Apr 23, 2024

Copy link to clipboard

Copied

Something like the following should work

function getStoriesInPageOrder() {
    var uniqueStories = {}; 
    var orderedStories = []; 
    var doc = app.activeDocument;

    for (var i = 0; i < doc.pages.length; i++) {
        var page = doc.pages[i];

        for (var j = 0; j < page.textFrames.length; j++) {
            var textFrame = page.textFrames[j];

            if (textFrame.isValid && textFrame.parentStory.isValid) {
                var storyId = textFrame.parentStory.id;
                if (!uniqueStories[storyId]) {
                    uniqueStories[storyId] = true;
                    orderedStories.push(textFrame.parentStory);
                }
            }
        }
    }
    return orderedStories;
}

var stories = getStoriesInPageOrder();
for (var s = 0; s < stories.length; s++) {
    $.writeln("Story " + (s + 1) + ": " + stories[s].contents);
}

-Manan

Votes

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
Community Expert ,
Apr 23, 2024 Apr 23, 2024

Copy link to clipboard

Copied

@Manan Joshi 

 

But in this code you are processing same Stories over and over again?

 

Let's say you have 100 pages and 2 Stories and 10 frames per page - 5 frames of each Story on each Page...

 

Votes

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
Community Expert ,
Apr 23, 2024 Apr 23, 2024

Copy link to clipboard

Copied

It is just looping over the frames and checking by id if the story is already accounted for or not. if it is accounted, we ignore it and if not then we add the story to our collection. How fast/slow the approach will be can be know after testing it. I thought you mentioned that the idea is not workable but it seems workable to me, how fast it will be is something that can be tested.

-Manan

Votes

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
Community Expert ,
Apr 23, 2024 Apr 23, 2024

Copy link to clipboard

Copied

@Manan Joshi

 

I never questioned if it's doable or not when going through Pages.

 

Votes

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
Community Expert ,
Apr 23, 2024 Apr 23, 2024

Copy link to clipboard

Copied

LATEST

Then maybe I misunderstood. Anyhow we have an alternative approach in the open now it is for the users to try and choose the one that suits them

-Manan

Votes

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