2
  1. First time posting
  2. First time writing in JavaScript, though I have experience in other languages.

I'm working in Adobe InDesign CS5.5. I have multiple files in an ID Book, each containing a varying number of "chapters". The book includes an index file with topic headings that reference the chapters in an abbreviated form (e.g., "CHAPTER 125" becomes "ch 125 no 3" -- note the "no x" part is irrelevant). The goal of my script is to create inter-document links that will add significant functionality when the ID Book is exported to, say, a PDF. The user will be able to jump from index to chapter and vice-versa. I think the script and the issues I'm dealing with would be of use to others but haven't found any posts to address my problem yet.

All refs (like "ch 125 no 1") in the index to a particular chapter ("CHAPTER 125") get a hyperlink to the location of the head of that chapter. This part of the script is working great and runs quickly.

The other half will insert the corresponding topic headings at the end of each chapter text and make those paragraphs link back to the corresponding topic head in the index. (In other words, they are cross references but not true x-refs in ID terms because I wanted more control over them and my reading on the topic told me to steer clear of true x-refs.) This is the part of the script that has me banging my head on the wall. It runs for hours upon hours without finishing a book of 200 chapters. Note that for testing purposes I am simply inserting one paragraph of text in the desired location under each chapter, rather than all topic heads and links. I know from smaller sets of text and from my debugging prints to the console that the script is doing work, not stuck in an infinite loop. Nevertheless, it runs way too long and, if I interrupt it, InDesign is unresponsive and I have to kill it, so cannot even review the partial results.

Based on searching/reading forums: I have disabled preflighting; disabled auto updating of book page numbers; changed the live preview settings to delayed. I still suspect the slowness may have to do with InDesign overhead but I don't know what else to try.

I'm embarrassed at how awful the style of this JS code might be but at the moment I just need it to work, then I can refine it.

var myBookFilePath = File.openDialog("Choose an InDesign Book File", "Indb files: *.indb");
var myOpenBook = app.open(myBookFilePath);
app.scriptPreferences.userInteractionLevel = UserInteractionLevels.neverInteract;

// Open up every file in the currently active Book
app.open(app.activeBook.bookContents.everyItem().fullName)

// TODO:  add error handling / user interaction here -- to pick which is Index file
var strIndexFilename = "Index.indd";
var objChapHeadsWeb = {};
var myDoc = app.documents.item(strIndexFilename);

$.writeln("\n\n~~~ " + myDoc.name + " ~~~");

// REMOVED CODE - check for existing hyperlinks, hyperlink sources/destinations
// loop to delete any pre-existing hyperlinks & associated objects
// works w/o any problems

// Ugly GREP to find the Main heading text (all caps entry and nothing beyond) in the index file
app.findGrepPreferences = NothingEnum.nothing;
app.changeGrepPreferences = NothingEnum.nothing;

/// GREP:  ^[\u\d \:\;\?\-\'\"\$\%\&\!\@\*\#\,\.\(\)]+[\u\d](?=\.|,)
app.findGrepPreferences.findWhat = "^[\\u\\d \\:\\;\\?\\-\\'\\\"\\$\\%\\&\\!\\@\\*\\#\\,\\.\\(\\)]+[\\u\\d](?=\\.|,)";
app.findGrepPreferences.appliedParagraphStyle = "Main";

var myFound = [];
myFound = myDoc.findGrep();
$.writeln("Found " + myFound.length + " Main headings.");

for (var i = 0; i < myFound.length; i++)   {
    myDoc.hyperlinkTextDestinations.add(myFound[i], { name: myFound[i].contents });
}

$.writeln("There are now " + myDoc.hyperlinkTextDestinations.count() + " destinations.");


myFound.length = 0;

for (var j = app.documents.count()-1; j >= 0; j--) {
    app.findGrepPreferences = NothingEnum.nothing;
    app.changeGrepPreferences = NothingEnum.nothing;

    // set the variable to the document we are working with
    myDoc = null;
    myDoc = app.documents[j];
    myFound.length = 0;

    if (myDoc.name === strIndexFilename) {
        continue;       // we don't want to look for chapter heads in the Index file, so skip it
    }

    $.writeln("\n\n~~~ " + myDoc.name + " ~~~");

// REMOVED CODE - check for existing hyperlinks, hyperlink sources/destinations
// loop to delete any pre-existing hyperlinks & associated objects
// works w/o any problems

    // Clear GREP prefs
    app.findGrepPreferences = NothingEnum.nothing;
    app.changeGrepPreferences = NothingEnum.nothing;

    app.findGrepPreferences.findWhat = "^CHAPTER \\d+";
    app.findGrepPreferences.appliedParagraphStyle = "chapter";

    myFound = myDoc.findGrep();
    var strTemp = "";
    $.writeln("Found " + myFound.length + " chapter headings.");

    for (var m = 0; m < myFound.length; m++)   {
        strTemp = myFound[m].contents;

        objChapHeadsWeb[strTemp] = {};
        objChapHeadsWeb[strTemp].withinDocName = myDoc.name;
        objChapHeadsWeb[strTemp].hltdChHead = 
            myDoc.hyperlinkTextDestinations.add(myFound[m], {name:strTemp});
        objChapHeadsWeb[strTemp].a_strIxMains = [];
        objChapHeadsWeb[strTemp].a_hltdIxMains = [];
        objChapHeadsWeb[strTemp].nextKeyName = "";

        objChapHeadsWeb[strTemp].nextKeyName = 
            ((m < myFound.length-1) ? myFound[m+1].contents : String(""));
    }

    $.writeln("There are now " + myDoc.hyperlinkTextDestinations.count() + " destinations.");
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//  Find the "ch" (chapter) references in the index file, link them
//      back to the corresponding text anchors for the chapter heads
//      in the text.
// 
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
myDoc = app.documents.item(strIndexFilename);   // work with the Index file

app.findGrepPreferences = NothingEnum.nothing;
app.changeGrepPreferences = NothingEnum.nothing;

// GREP to find the "ch" (chapter) references in the index file
// like ch 151 no 1 OR ch 12 no 3
app.findGrepPreferences.findWhat = "(ch\\s+\\d+\\s+no\\s+\\d+)";

var strExpandedChap = "";
var strWorkingMainHd = "";
var arrFoundChapRefs = [];
var myHyperlinkSource;
var myHyperlinkDest;

for (var x = 0; x < myDoc.hyperlinkTextDestinations.count(); x++)   {
    strWorkingMainHd = "";
    arrFoundChapRefs.length = 0;

    // the special case, where we are working with the ultimate hyperlinkTextDestination obj
    if (x === myDoc.hyperlinkTextDestinations.count()-1) {
        // This is selecting text from the start of one MAIN heading...
        myDoc.hyperlinkTextDestinations[x].destinationText.select();
        // This next line will extend the selection to the end of the story,
        //      which should also be the end of the document
        myDoc.selection[0].parentStory.insertionPoints[-1].select(SelectionOptions.ADD_TO);
    }
    // the regular case...
    else  {
        // This is selecting text from the start of one MAIN heading...
        myDoc.hyperlinkTextDestinations[x].destinationText.select();
        // ... to the start of the next MAIN heading
        myDoc.hyperlinkTextDestinations[x+1].destinationText.select(SelectionOptions.ADD_TO);
    }

    strWorkingMainHd = myDoc.hyperlinkTextDestinations[x].name;
    //arrFoundChapRefs = myDoc.selection[0].match(/(ch\s+)(\d+)(\s+no\s+\d+)/g);  //NOTE:  global flag

    arrFoundChapRefs = myDoc.selection[0].findGrep();

    for(y = 0; y < arrFoundChapRefs.length; y++)    {
        myHyperlinkSource = null;
        myHyperlinkDest = null;
        strExpandedChap = "";

        strExpandedChap = arrFoundChapRefs[y].contents.replace(/ch\s+/, "CHAPTER ");
        strExpandedChap = strExpandedChap.replace(/\s+no\s+\d+/, "");

        // if we found the chapter head corresponding to our chapter ref in the index
        //      then it is time to create a link
        if (strExpandedChap in objChapHeadsWeb)    {
            objChapHeadsWeb[strExpandedChap].a_strIxMains.push(strWorkingMainHd);
            objChapHeadsWeb[strExpandedChap].a_hltdIxMains.push(myDoc.hyperlinkTextDestinations[x]);

            myHyperlinkSource = myDoc.hyperlinkTextSources.add(arrFoundChapRefs[y]);
            myHyperlinkDest = objChapHeadsWeb[strExpandedChap].hltdChHead;

            myDoc.hyperlinks.add(myHyperlinkSource, myHyperlinkDest);
        }   else    {
            $.writeln("Couldn't find chapter head " + strExpandedChap);
        }
    }
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// NOW TIME FOR THE HARD PART...
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
myDoc = null;
var strWorkingMainHd = "";
var nextKey = "";
var myParentStory = null;
var myCharIndex = 0;
var myCompareChar = null;
var myLeftmostBound = 0;
var myCurrentPara = null;

for (var key in objChapHeadsWeb)   {
    myDoc = app.documents.item(objChapHeadsWeb[key].withinDocName);
    myCompareChar = null;   //recent addition
    $.writeln("Working on " + key + ".");   //debugging

    nextKey = objChapHeadsWeb[key].nextKeyName;

    objChapHeadsWeb[key].hltdChHead.destinationText.select();
    myLeftmostBound = myDoc.selection[0].index;
    myParentStory = myDoc.selection[0].parentStory;

    if( (nextKey === "") || (myDoc.name !== objChapHeadsWeb[nextKey].withinDocName) )
    {
        //// Need to find end of story instead of beginning of next chapter
        //myDoc.selection[0].parentStory.insertionPoints[-1].select(SelectionOptions.ADD_TO);
        myParentStory.insertionPoints[-1].select();
        //myCharIndex = myDoc.selection[0].index;           /recently commented out

        myCharIndex = myDoc.selection[0].index - 1;     //testing new version
        myCompareChar = myParentStory.characters.item(myCharIndex);     //recenttly added/relocated from below
    }   else    {
        /////
        //objChapHeadsWeb[nextKey].hltdChHead.destinationText.select(SelectionOptions.ADD_TO);
        objChapHeadsWeb[nextKey].hltdChHead.destinationText.select();

        //myParentStory.characters.item(myDoc.selection[0].index -1).select();

        myParentStory.characters.item(myDoc.selection[0].index -2).select();  //temp test *****

        myCharIndex = myDoc.selection[0].index;
        myCompareChar = myParentStory.characters.item(myCharIndex);

        if (myCompareChar.contents === "\uFEFF") {
            $.writeln("Message from inside the \\uFEFF check.");     //debugging

            myParentStory.characters.item(myDoc.selection[0].index -1).select();

            myCharIndex = myDoc.selection[0].index;
            myCompareChar = myParentStory.characters.item(myCharIndex);
        }

        if( (myCompareChar.contents !== SpecialCharacters.PAGE_BREAK) &&
            (myCompareChar.contents !== SpecialCharacters.ODD_PAGE_BREAK) &&
            (myCompareChar.contents !== SpecialCharacters.EVEN_PAGE_BREAK) &&
            (myCompareChar.contents !== SpecialCharacters.COLUMN_BREAK) &&
            (myCompareChar.contents !== SpecialCharacters.FRAME_BREAK)) 
        {
            $.writeln("Possible error finding correct insertion point for " + objChapHeadsWeb[key].hltdChHead.name + ".");
        }
    }

    if(myCharIndex <= myLeftmostBound)  {   // this shouldn't ever happen
        alert("Critical error finding IX Marker insertion point for " + objChapHeadsWeb[key].hltdChHead.name + ".");
    }

    if(myCompareChar.contents !== "\r") {
        myDoc.selection[0].insertionPoints[-1].contents = "\r";
    }

    myDoc.selection[0].insertionPoints[-1].contents = "TESTING text insertion for:  " + objChapHeadsWeb[key].hltdChHead.name + "\r";
    myDoc.selection[0].insertionPoints.previousItem(myDoc.selection[0].insertionPoints[-1]).select();

//myDoc.selection[0].insertionPoints[-1].contents = "<Now I'm here!>";

    myCurrentPara = myDoc.selection[0].paragraphs[0];

    myCurrentPara.appliedParagraphStyle = myDoc.paragraphStyles.item("IX Marker");

    // TODO:
    //      need error handling for when style doesn't already exist in the document
}   // end big for loop


//TODO:  add error handling support to carry on if user cancels
//close each open file; user should be prompted to save changed files by default

app.scriptPreferences.userInteractionLevel = UserInteractionLevels.interactWithAll;
app.documents.everyItem().close();

// Cleanup
app.findGrepPreferences = NothingEnum.nothing;
app.changeGrepPreferences = NothingEnum.nothing;
wgarr237
  • 21
  • 1
  • 3

2 Answers2

0

Try open all files of Cross References are linking to them.

D T
  • 3,522
  • 7
  • 45
  • 89
0

May I suggest a few improvements that can probably speed up things a bit. First of all you have tons of global variables here that you may concentrate in much less scopes using functions. Having many global variables has a big cost in terms of performances.

Once that said, I won't open every single doc of the book at once but process them one by one. Be aware that grep calls are very cost expensive so you may try to look at your patterns.

Another one is the extensive use of the $.writeln command. Avoid it especially within loops. Prefer an easy to set report library.

Finally I tried to rewrite your code in a "better" way but it was hard to construct the whole script with a clear understanding of your needs and no files to process. But I hope the following snippet will help you to start rewriting your code and state significant time improvements.

var debug = true;

var log = function(msg) {
 
 var l = File (Folder.desktop+"/log.txt" );
 
 if ( !debug ) return;
 
 l.open('a');
 l.write(msg);
 l.close();
};

var main = function() {
 
 var bookFile, uil = app.scriptPreferences.userIntercationLevel;
 
 log("The party has started");
 
 bookFile = File.openDialog("Choose an InDesign Book File", "Indb files: *.indb");
 
 if (!bookFile) return;
 app.scriptPreferences.userInteractionLevel = UserInteractionLevels.NEVER_INTERACT;
 
 try {
  processBookFile ( bookFile );
 }
 catch(err) {
  alert(err.line+"///"+err.message);
 }

 app.scriptPreferences.userInteractionLevel = uil;
};

function processBookFile ( bookFile ) {
 var book = app.open ( bookFile ),
 bks = book.bookContents,
 n = bks.length;
 
 while ( n-- ) {
  File(bks[n].name)!="Index.indd" && processBookContent ( bks[n] );
 }
}

function processBookContent ( bookContent ) {
 var bcf = bookContent.fullName,
 doc = app.open ( bcf, debug );
 
 //DEAL WITH HEADINGS
 processHeadings ( doc );
 
 //DEAL WITH CHAPTERS
 processHeadings ( doc );
 
 //add hyperlinks
 addHyperlinks( doc);
}



function processHeadings (doc){
 var props = {
  findWhat : "^[\\u\\d \\:\\;\\?\\-\\'\\\"\\$\\%\\&\\!\\@\\*\\#\\,\\.\\(\\)]+[\\u\\d](?=\\.|,)",
  appliedParagraphStyle : "Main"
 },
 found = findGrep(doc, props),
 n = found.length;
 
 while ( n-- ) {
  doc.hyperlinkTextDestinations.add(doc, { name: found[i].contents });
 }
};

function processChapters (doc ) {
 var props = {
  findWhat : "^CHAPTER \\d+",
  appliedParagraphStyle : "chapter"
 },
 found = findGrep(doc, props),
 n = found.length;
 
 while ( n-- ) {
  doc.hyperlinkTextDestinations.add(found[n], found[n].contents);
 }
}

function findGrep(doc, props){
 app.findGrepPreferences = app.changeGrepPreferences = null; 
 app.findGrepPreferences.properties = props;
 return doc.findGrep();
}

function addHyperlinks (doc){
 //a logic of yours
};


main();
Loic
  • 2,173
  • 10
  • 13