Friday, January 11, 2008

 

Speeding up a Script

Most of the time, I pay scant attention to how long a script takes to run because most of them are so quick it hardly matters, but every now and then a simple script comes along that takes serious time to run. I just hit one as I sit here working on a catalog. I'm getting near the end of the job, making corrections and changes (mainly changes).

I realized that I've done so much dickering around to get things to fit on the page in earlier passes that it's time to reset the space before/after of every paragraph. Well, that's easy:
//DESCRIPTION: Restore vertical paragraph style spacing

if (app.documents.length > 0 &&
app.selection.length == 1 &&
app.selection[0].hasOwnProperty("baseline")) {
restoreParagraphSpacing(app.selection[0]);
}

function restoreParagraphSpacing(myText) {
var myParas = myText.paragraphs;
for (var j = 0; myParas.length > j; j++) {
myParas[j].spaceBefore = myParas[j].appliedParagraphStyle.spaceBefore;
myParas[j].spaceAfter = myParas[j].appliedParagraphStyle.spaceAfter;
}
}
If I were just running this on the odd page here and there, it wouldn't take too long, but document has 43 pages and the story has 782 paragraphs, so this loop interacts with InDesign 1564 times.

Clearly, we could cut that in half by changing the loop to this:
  for (var j = 0; myParas.length > j; j++) {
myParas[j].properties = {
spaceBefore:myParas[j].appliedParagraphStyle.spaceBefore,
spaceAfter:myParas[j].appliedParagraphStyle.spaceAfter
}
}
But this still requires 782 interactions with InDesign. And all of them happen even if the script does nothing because I'm running it for a second time. There's one more thing worth trying:
//DESCRIPTION: Restore vertical paragraph style spacing

if (app.documents.length > 0 &&
app.selection.length == 1 &&
app.selection[0].hasOwnProperty("baseline")) {
restoreParagraphSpacing(app.selection[0]);
}

function restoreParagraphSpacing(myText) {
var myParas = myText.paragraphs;
var mySBs = myParas.everyItem().spaceBefore;
var mySAs = myParas.everyItem().spaceAfter;
for (var j = 0; myParas.length > j; j++) {
if (myParas[j].spaceBefore != mySBs[j] || myParas[j].spaceAfter != mySAs[j]) {
myParas[j].properties = {
spaceBefore:myParas[j].appliedParagraphStyle.spaceBefore,
spaceAfter:myParas[j].appliedParagraphStyle.spaceAfter
}
}
}
}
The script still does a lot of interacting, but most of it is reading. It only writes a value into the document if there is a change needed, so right now the script runs very quickly because there are no longer any changes to be made.

Ah well, back to work!

Saturday, January 05, 2008

 

Instructive Confusion

The other day, I went so far as to report a bug to Adobe only to discover that I was missing the obvious. It wasn't a bug I was experiencing, but it surely would make a nice feature request. I was working with a book and I had all the documents open. But one of the documents was of primary interest to me at that point in time, and I had two windows open into it, arranged to be side-by-side on my screen.

When I double-clicked that document in the book window, I expected both windows to come to the front. But only one did. And, as luck would have it, it was the one on the left that I had temporarily forgotten about. I was expecting the window on the right to come to the front. When it didn't after repeated tries, I fired off a bug report to Adobe, only to feel rather silly a few minutes later when I realized that the window at left was indeed a window of the document I was trying to wake up. So, I modified my report and made it a feature request instead.

Anyway, it being Saturday evening and I'm sitting here amusing myself messing around with scripts while listening to Mahler's Ninth Symphony, it occurred to me that I could write a script to bring all the windows of the active document to the front. I decided that I wanted them to retain whatever stacking order they already had, so I thought that all that meant was I should cycle through all the active document's windows and bring them to the front starting with the back one.

It came as quite a blow when this script didn't work:
//DESCRIPTION: Bring all Windows of active document to the front

if (app.windows.length > 0) {
bringToFront(app.windows[0].parent);
}

function bringToFront(aDoc) {
var myWindows = aDoc.windows;
for (var j = myWindows.length - 1; j >= 0; j--) {
app.activeWindow = myWindows[j];
}
} // end bringToFront
In as much as it did bring all the active document's windows to the front, you could say it did work, but it reversed the order of the windows. That which was at the back was now at the front (I only had two windows open; with more the result would have been more muddled).

Why is this? Because myWindows is pointing at the collection of windows owned by the active document, so each time I changed the order of the windows, the order of their references in in myWindows also changed. I've fallen into this trap so many times with objects of various sorts, you'd think I'd see it coming. The solution is to deploy getElements() to create an array that captures a snapshot of the order of the windows before we perform the loop, like this:
//DESCRIPTION: Bring all Windows of active document to the front

if (app.windows.length > 0) {
bringToFront(app.windows[0].parent);
}

function bringToFront(aDoc) {
var myWindows = aDoc.windows.everyItem().getElements();
for (var j = myWindows.length - 1; j >= 0; j--) {
app.activeWindow = myWindows[j];
}
} // end bringToFront
Now, the contents of myWindows is not affected by the changes made in the loop and so the windows end up in the order I wanted them, at the front of any other windows that might be open into other documents.

Note: app.windows[0].parent is equivalent to app.documents[0], but this is a script about windows, so I used the window-based construction.

This page is powered by Blogger. Isn't yours?