Reordering NCX playOrder

  • Sumo

When you make changes to the TOC.NCX file in an ePUB—moving front matter to the back of the ePUB; adding additional content—one of the frustrations is that the playOrder in the TOC.NCX file has to be renumbered. If your book has a short TOC.NCX this might not be a challenge, but if you have a long book or have nested multiple levels of heads in the TOC.NCX, this can be very time-consuming to do manually. I’ve been looking for a way to automatically reorder the playOrder for some time. David Golding (@digidg) posted a couple links on Twitter to scripts that will do just that. One is in XSL; the other is in Vim. Rick Gordon (@rhgordon) has an AppleScript that will do the same. These should makes things easier the next time you need to renumber your TOC.NCX.

I’m still looking for the elusive Perl script that is reported to be out there. If you have one or know where to find one, please leave the URL in the comments. Do you have other script your use for reordering the playOrder?

28 Responses to “Reordering NCX playOrder”

  1. incblotter says:

    Any chance you could add a brief tutorial on using the AppleScript for those not familiar with the process?

  2. @aarontroia says:

    I have tried Rick Gordan’s AppleScript and it will throw errors if you are using a Mac with Lion installed.

  3. Thanks!
    Automating this process is a huge timesaver

  4. Martin says:

    I wrote you a small python script that does this kind of renumbering:

    import re
    i = 0
    def replace(m): global i; i += 1; return str(i)
    s = open(‘toc.ncx’, ‘rb’).read()
    s = re.sub(r'(?<= playOrder=")(\d+)', replace, s)
    open('toc_renumbered.ncx', 'wb').write(s)

    Usage: copy and paste the code above to a text file called and place it in the same folder as the toc.ncx file (the ncx needs to be called exactly that). If you are on mac/unix (which have case-sensitive file names) and have used upper-case letters (TOC.NCX) you need to modify the python script accordingly. Then run it by typing "python" on the command line.

    For safety the output is written to a separate file called "toc_renumbered.ncx". If you are happy with the result you can simply switch the two files when you're done.

  5. Here is the Ruby one-liner that I use:
    ruby -pi.bak -e “BEGIN{\$num=0}; gsub(/(playOrder=.)\d*/) {|s| \$num+=1; \$1+\$num.to_s}” *.ncx

    And here it is translated into Perl:
    perl -pi.bak -e ‘BEGIN{$num=1} s/(playOrder=.)\d*/sprintf(“%s%d”,$1,$num++)/e’ toc.ncx

    Both tested in the Mac’s Terminal (i.e. bash) with Ruby 1.8.6 and Perl 5.8.8. I’m sure just about anyone familiar with Ruby or Perl could do a more elegant job.

  6. Thanks for the additional scripts. Anyone else have one to add? How about an update to that AppleScript that will work on Lion, anyone have any thoughts? We will definitely put a walk-through of running AppleScripts to the future post list.

  7. incblotter, there are some nice posts on AppleScript at InDesignSecrets.

    “AppleScript: Why InDesign Users Should Learn It”

    “Want to Learn AppleScript? Check Out This Book”

    “Drag and Drop AppleScripts for EPUB, IDML, etc.”

    You might also want to check out AppleScript Pro Sessions here:


    Of all the ones mentioned (and I tried them all), I found that the Python script was the easiest to make work for me, but first it needed a couple of corrections. Microsoft Word’s smart quotes setting has reared its ugly head in Martin’s great little script above. The curly quotes in lines 4 and 5 need to be replaced with straight quotes, or you will get a Syntax Error (I did, until I noticed what was wrong), so:

    (‘toc.ncx’, ‘rb’)


    (‘toc.ncx’, ‘rb’)





    Also, Python sometimes likes you to declare, in the first or second line, a source code encoding for your script. This is discussed nicely at:

    I chose to use:

    # coding: UTF-8

    Thus, my corrected script is:

    # coding: UTF-8
    import re
    i = 0
    def replace(m): global i; i += 1; return str(i)
    s = open(‘toc.ncx’, ‘rb’).read()
    s = re.sub(r'(?Open…” your script), but, like any programming language (including HTML), the steps ARE specific, so I will be discussing this further in the next couple of days on my blog at:

    Thanks, Martin, for a really easy solution to a vexing problem.

  9. I stand corrected. I see now that it’s not the smart quotes setting of Word; it’s the blogging environment that publishes these posts. Sorry, Martin. So, guys, as you do your cut-and-paste of any of these scripts, make sure that your quotes are all straight ones.

  10. Matthew says:

    Peter, thanks for pointing out this issue so the script will work for anyone who runs it.

  11. […] the added items in the NCX (for tips on using scripts to renumber the NCX, see our earlier post “Reording NCX playOrder”). Theoretically, you can nest to as deep a level as you want. Practically, you may want to limit it […]

  12. Not sure if I’m late to the party, but here’s a simple bit of JavaScript that performs an NCX reorder that I use for my workflow. For the corresponding HTML, the id=”ta” will go inside a textarea element where you can paste in the old unordered ncx and it outputs the ordered ncx after triggering the “go” function with a button or something else.

    If anyone’s interested I can put this online to make it more user-friendly.

    function go() {
    temp1 = document.getElementById(“ta”).value;
    var i = 0;
    var j = 0;
    var z = temp1.lastIndexOf(‘playOrder=”‘);
    var x = 1;
    while (i<=z)
    i = temp1.indexOf('playOrder="', i);
    if (i == -1){break;}
    i= i + 11;
    j = temp1.indexOf('"', i);
    temp1=temp1.substring(0, i) + x + temp1.substring(j);
    document.getElementById("ta").value = temp1;

  13. Matthew says:

    Paul, thanks for posting your NCX reordering JavaScript. It’s another good addition to the ePUB creation toolbox.

  14. Thanks! This worked for me, too, with slight adjustments:
    import re
    i = 0
    def replace(m): global i; i += 1; return str(i)
    s = open(“toc.ncx”, “rb”).read()
    s = re.sub(r”(?<= playOrder=)(\d+)", replace, s)
    open("toc_renumbered.ncx", "wb").write(s)

    Weirdly, my version of Python wanted me to delete the inch marks after "playOrder="

    I was able to tell Python to ignore the coding declaration, though Idle groused at me.

  15. Matthew says:

    I’m glad you were able to make it work. Thanks for sharing the adjustments you needed to make.

  16. Lara Smith says:

    I tried using the Apple Script (OS 10.6.8) and it almost worked.
    I just picked up Soghoian’s AppleScript 1-2-3, but it’s too early for me to try to tweak the script. Using Rick Gordon’s sample ncx text and AppleScript Editor I got this result:

    Tips on Using This Book…

    turns into this:

    Tips on Using This Book…

    Does any one have an alternative AppleScript?

  17. Lara Smith says:

    Oops. This time without angle brackets. Basically, a new number was introduced, but it did not replace the old playOrder number. And the navLabel was removed.

    playOrder=”4″ navLabel text


    playOrder=”4″ 1 text

    Any thoughts?

  18. Matthew says:

    Are you on a Mac with Lion OSX? According to @aarontroia above the AppleScript will throw errors on Lion. It appears the script is not iterating (the i in the script) on the playOrder but rather on the navLabel. If you copied that text out of the text, you might also want to look at your quote marks and make sure they are consistent.

  19. Lara Smith says:

    Thanks for your reply Matthew. I’m not running Lion, so I’ll keep investigating. In the meantime, if I can get the script to run on my .ncx the way it is, I could at least fix it up with a GREP search.

  20. Lara Smith says:

    Aha. Looks like there’s an extra “word after” in the script, which explains why it’s affecting the navLabel instead of the playOrder.

    (word after word after vPlayOrderWord))

    It works fine now on short tests—but TextEdit hangs when I apply it to my 550-entry TOC…

  21. Using the Python script in OS X 10.9 Mavericks, I get an error:

    File "", line 7
    s = re.sub(r"(?<= playOrder=")(\d+)", replace, s)
    SyntaxError: unexpected character after line continuation character

    Any ideas as to how to make it work?

  22. Display error: the caret pointing to the error is supposed to at the end of the previous line.

  23. I’m not sure why but thiѕ weblog is loading extremelƴ slow for
    me. Is anyone else having this issue or is іt a ƿroblem on my end?

    I’ll check back later on and see if the prօblem sgill exists.

    Stop bby my homepage :: foodstuff saver jar, meal hard drive, saver pot, dishes storage

  24. Philo007 says:

    Hi Matthew, did you find a solution? Did you find the script “Perl script”?
    I have a solution in javascript.

    Bonjour Matthew, avez-vous trouver une solution ? avez-vous trouver le script “Perl script”?
    J’ai une solution en javascript.

  25. mateo sánchez says:

    I use the number_adjuster_1_2.jsx script in indesign. Sumo or rest as needed.

    Copy / paste in indesign, beware of the “!!”, and set it.

    Yo uso el script de number_adjuster_1_2.jsx en indesign. Sumo o resto según necesite.

    copio/pego en indesign, ¡¡cuidado con las “!!, y ajusto.

    Un saludo

  26. […] you’ve seen this script […]

  27. […] you’ve seen this script […]

  28. talla says:

    my solution is to delete this attribute, playOrder is optional!

    in vim:

    :%s/ playOrder=”[^”]*”//