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
main(); 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: function main() { var myDoc = app.activeDocument; var myList; myList = myDoc.pages.everyItem().name; var myDialog = app.dialogs.add({name:"SwapPages", canCancel:true}); with(myDialog){ //Add a dialog column. with(dialogColumns.add()){ with (dialogColumns.add()) { staticTexts.add({ staticLabel: "swap page " }); } with (dialogColumns.add()) { //Create a pop-up menu ("dropdown") control. var myPageDropdown0 = dropdowns.add({ stringList: myList, selectedIndex: 0 }); } with (dialogColumns.add()) { staticTexts.add({ staticLabel: " with page " }); } with (dialogColumns.add()) { //Create a pop-up menu ("dropdown") control. var myPageDropdown1 = dropdowns.add({ stringList: myList, selectedIndex: 0 }); } } } if(myDialog.show() == true){ swapPages(myList[myPageDropdown0.selectedIndex], myList[myPageDropdown1.selectedIndex]); myDialog.destroy(); } else{ myDialog.destroy(); } }Thanks for this useful addition.
@+
Marc