Swapping and Reordering Pages
December 01, 2009 | Snippets | en
The InDesign Pages panel lets you manually rearrange the pages of your publication. To move pages around in the work document, it's just needed to click and drag the page icon(s) within the railroad —while paying attention to ‘shuffling’ effects! JavaScript allows you to automate the same handling, which can be very useful when you have a number of pages to switch.
The JavaScript DOM attaches to the Page
object a move
method specified as follows:
Page.move (to: LocationOptions, reference: Page|Spread, binding: BindingOptions): Page
where:
• to
is a LocationsOptions
enumerator (among .AFTER
, .BEFORE
, .AT_BEGINNING
, .AT_END
, .UNKNOWN
) indicating “the new location of the page relative to the reference object or within the document or spread”,
• reference
is the Page
or Spread
object in relation to the previous argument when specified by LocationsOptions.AFTER
or LocationsOptions.BEFORE
,
• binding
specifies (if needed) “the location of the binding spine in spreads”: BindingOptions.LEFT_ALIGN
, BindingOptions.RIGHT_ALIGN
or BindingOptions.DEFAULT_VALUE
.
There's no difficulty in invoking repeatedly the Page.move()
method except that you need to care about the page reference changes during the process. Suppose you want to swap page 4 and page 7. An intuitive way to do this is to move page 7 after page 4, then to move page 4 after page 7 . . . Hey, wait a minute! Page 7 is no more the 7th page after the first step:
So we must recalculate the page reference.
How to get a page reference
When querying a Page
object from a Pages
collection, it's very important to make a distinction between the “page name” and the “page number”. In fact, the page number is an illusion. Because of the contingent section prefix, JavaScript handles the Page.name
property as a String
. Thus, assuming no section prefix is applied, the good way to get a page from its ‘number’ is to convert the Number
into a String
before invoking explicitly the Pages.itemByName()
method. Example: myPage = myDoc.pages.itemByName(''+myNumber)
.
On the other hand, any page has a documentOffset
property which is the zero-based index of the page within the document. But there are two pitfalls to avoid: confusing Page.index
and Page.documentOffset
; and querying the Pages getters —like Pages[]
and Pages.item()
— without consideration for implicit type conversions. Since the Page.index
property only returns the page index within its spread container, it's often useless when you target the document. To understand the second fact, let's compare various access methods:
var pages = app.activeDocument.pages; alert( pages[3].name ); // displays: 4 alert( pages["3"].name ); // displays: 4 alert( pages.item(3).name ); // displays: 4 alert( pages.item("3").name ); // displays: 3 alert( pages.itemByName(3).name ); // ERROR alert( pages.itemByName("3").name ); // displays: 3
We can conclude that:
• The square bracket operator Pages[]
always expects the page zero-based index as a Number
, and will implicitly convert anything else into a Number
.
• The Pages.item()
method expects either a zero-based index (Number
) or the page name (String
), so the result depends on the type you provide.
• The Pages.itemByName()
method expects the page name (String
) and does not perform type conversion.
Anyway, you need to keep in mind that Number(myPage.name)
is not necessary equal to myPage.documentOffset+1
regarding the Section.pageNumberStart
option.
Also, because of the Spread.allowPageShuffle
, DocumentPreference.allowPageShuffle
and DocumentPreference.preserveLayoutWhenShuffling
constraints, Page.move()
can underperform when used between gatefold spreads. (In the next section, it's supposed we are working with facing-page or single-sided multipage documents.)
Swapping two pages
Here's a very simple code to perform page swapping:
function swapPages(p0, p1) { if (p0 == p1) return true; var pages = app.activeDocument.pages; var pg0 = pages.itemByName(''+p0); // force string argument var pg1 = pages.itemByName(''+p1); // force string argument if ( pg0.documentOffset > pg1.documentOffset ) return swapPages(p1,p0); try { pg1.move(LocationOptions.AFTER,pg0); pg0.move(LocationOptions.AFTER,pages.itemByName(''+p1)); return true; } catch(ex) {return false;} } // usage: swapPages(4,7);
The swapPages()
function expects two page numbers —in fact, two page names— given as String
or Number
. The implementation uses the documentOffset
property only to check the initial page order.
Reordering all the pages of a document
The following script (for ID CS4) is more ambitious. It provides an interface allowing the user to rearrange the whole document by simply inputting a ‘reordering’ string. For example, 4, 7-5, 8, 1-3
remaps a 8-page document according to the sequence: 4, 7, 6, 5, 8, 1, 2, 3.
function reorderPages(/*str*/newOrder) //-------------------------------------- { var pages = app.activeDocument.pages; var pgMap = (function() { var items = newOrder.replace(/[^\d,-]/g,'').split(','); var r = [], bkp={}, i, i0, i1, sz, p, inc; while(i=items.shift()) { i = i.split('-'); // don't allow "x-y-z" if ( (sz=i.length) > 2 ) return "Invalid range: '"+i.join('-')+"'"; i0 = Number(i[0]); i1 = Number(i[sz-1]); inc = ( i0 > i1 ) ? -1 : 1; for( p=i0 ; p*inc <= i1*inc ; p+=inc ) { if (!(pages.item(''+p).isValid)) return "The page "+p+" doesn't exist!"; if ((''+p) in bkp) return "The page "+p+" is specified twice!"; bkp[''+p]=1; r.push(pages.item(''+p).id); } } return r; })(); if ( typeof pgMap == 'string') return pgMap; var sz = pgMap.length; if (sz != pages.length) return "Page number mismatch -- "+pgMap.length+" given, "+pages.length+" in document"; // at this point, {pgMap} contains a valid permutation app.scriptPreferences.enableRedraw = false; for(var i=0 ; i < sz ; i++ ) { pages.itemByID(pgMap[i]).move(LocationOptions.AT_END); } app.scriptPreferences.enableRedraw = true; return true; } function main() //-------------------------------------- { if (!app.documents.length) {alert("Please open a document");return;} if (app.activeDocument.pages.length < 2) {alert("The document has a single page!");return;} var order = (function() { return this.lastItem().name + '-' + this.firstItem().name; }).call(app.activeDocument.pages); var ret; while(true) { order = prompt("Enter the new order of the pages using page numbers and/or page ranges separated by commas (e.g. 4, 7-5, 8, 1-3):", order, " Reorder Pages | \u00A9Indiscripts.com"); if (order === null) return; ret = reorderPages(order); if( ret === true ) return; alert(ret); } } main();
The reorderPages()
function exploits an interesting fact: when moving a page around the document, the internal id of the page —Page.id
— remains invariant. Thus, storing the page ids in an array before using the function provides permanent object references.
The script above has been tested for a long document (200+ pages) and should still provide good performances for more pages. We explored a few variants in the InDesign Scripting Forum.
Comments
Un grand merci Marc car même si je ne penses pas avoir besoin de ce script maintenant, l'application que tu mets à fournir moults explications sans compter le temps passé est un effort à saluer.
Si nous progressons tous dans l'apprentissage du scripting Indesign, nous te le devons pour beaucoup.
Donc mille mercis :-)
Merci Loïc pour tes encouragements à poursuivre dans cette voie pédagogique.
@+
Marc
The easiest way of preserving index-based object references in InDesign even when objects are moved, is to convert a collection to an array using getElements() this returns <strong>absolute</strong> (i.e. id based) references to the objects instead of the index-based ones.
Hi, at first thanx for the post. And second i edited the swapPages function a bit to have a little userinterface.
Makes the usage easier.
cheers
:F
Thanks for this useful addition.
@+
Marc