Search code examples
adobe-indesign

Script to find footnotes in text and transform them into real footnotes


I am trying to format a text in InDesign that was originally written in LaTex, so I have all the footnotes in the main text surrounded by \footnote{} tags:

This is my main text\footnote{with foonotes and sometimes with other braces inside footnotes such as\ref{section2}}, and I want to format it properly.

I have hundreds of notes so I would like to write a script that can format them automatically. I am using the one below (found here https://gist.github.com/welblaud/c21a96f2f23db58b4011726cf21addb8) but so far I’m unable to adapt it to match my pattern.

What am I missing?

Application.prototype.main = function(){
  if (this.documents.length <= 0) return;
  var tg = this.selection[0] || this.activeDocument;
  if ('appliedFont' in tg) tg = tg.parent;
  if (tg.constructor == TextFrame) { tg = tg.parentStory; }
  if (!('findGrep' in tg)) return;

  /* Here it is neecessary to choose a pattern for wrapping the note, we are not parsing
  here, hence the rough technique! */
  var fnPatterns = ["(\\footnote\{)([^\}]+)(\})"];

  var count = 0;

  for(patterCounter = 0; patterCounter < fnPatterns.length; patterCounter++){
    fnPattern = fnPatterns[patterCounter];

    var fnFinds = (function(){
      this.findGrepPreferences = this.changeGrepPreferences = null;
      this.findGrepPreferences.findWhat = fnPattern;
      var ret = tg.findGrep();
      this.findGrepPreferences = this.changeGrepPreferences = null;

      return ret;
    }).call(this);

    var fnFind, fnPrefix,fnSuffix, rg = new RegExp(fnPattern), ip, fnParent, fn, count;

    while (fnFind = fnFinds.pop()){
      fnPrefix = fnFind.contents.match(/^@[^@]+@/)[0];
      fnSuffix = fnFind.contents.match(/@[^@]+@/)[0];

      try {
        // add footnote
        fn = fnFind.footnotes.add(LocationOptions.AFTER, fnFind.insertionPoints[-1]);
        // duplicate the text
        fnFind.texts[0].characters.itemByRange(fnPrefix.length,fnFind.texts[0].characters.length-fnSuffix.length-1).duplicate(LocationOptions.AT_END, fn.texts[0]);
        // remove the original
        fnFind.characters.itemByRange(0,fnFind.characters.length-2).remove();
        ++count;
      }
      catch(_){}
    }
  }
  alert((count)? (count+" footnote(s) successfully added."): "No footnote added. Make sure you use the relevant pattern.");
}
app.doScript('app.main();', ScriptLanguage.javascript, undefined, UndoModes.entireScript, app.activeScript.displayName);

Solution

  • Try to replace:

    var fnPatterns = ["(\\footnote\{)([^\}]+)(\})"];
    

    with:

    var fnPatterns = ["\\\\footnote\\{.+?\\}"];
    

    And:

    fnPrefix = fnFind.contents.match(/^@[^@]+@/)[0];
    fnSuffix = fnFind.contents.match(/@[^@]+@/)[0];
    

    with:

    fnPrefix = '\\footnote{';
    fnSuffix = '}';
    

    It should handle all footnotes without the nested \ref{...} tags.

    To handle the nested tags I'd propose to replace their {} with something else. With @ for example. And change them back to {} after the main code is done. It can be done if you add the several lines before and after the app.doScript():

    // change '\ref{...}' --> '\ref@...@'
    app.findGrepPreferences = app.changeGrepPreferences = null;
    app.findGrepPreferences.findWhat = "\(\\\\ref\)\\{\(.+?\)\\}";
    app.changeGrepPreferences.changeTo = "$1@$2@";
    app.activeDocument.changeGrep();
    
    // run the script
    app.doScript('app.main();', ScriptLanguage.javascript, undefined, UndoModes.entireScript, app.activeScript.displayName);
    
    // change '\ref@...@' --> '\ref{...}'
    app.findGrepPreferences = app.changeGrepPreferences = null;
    app.findGrepPreferences.findWhat = "\(\\\\ref\)@\(.+?\)@";
    app.changeGrepPreferences.changeTo = "$1\{$2\}";
    app.activeDocument.changeGrep();
    

    If you have another nested tags you can toggle them about the same way.

    But if you have nested tags inside nested tags \ref{...\subref{...}...} it may require a more complicated recursive solution or a particular sequence of substitutions (frist from inner tags to outer tags and then in reverse order, probably).

    This is why, by the way, the start and end tags <a>...<b>...</b>...</a> work better than brackets {...{...}...}.