Tag Archives: FrameScript

Running a FrameScript Script from DITA-FMx

As a follow-up to Custom scripting in the DITA-FMx book-build process (which focuses on using ExtendScript), I asked Rick Quatro (FrameScripter extraordinaire) if he could provide some detailed information on setting up a FrameScript for use in the DITA-FMx book-build process.

The following is a repost (with permission) of that article (also available on Rick’s website).


DITA-FMx is a great plugin for working with DITA in FrameMaker. One of its best features is the ability to create a complete FrameMaker book from a ditamap. In some situations you may want to run a script on the book before creating the PDF. Scott Prentice, the developer of DITA-FMx, has a blog post explaining how you can call an ExtendScript script from DITA-FMx. This article will show you how to call a FrameScript script from DITA-FMx.

To set this up in DITA-FMx, you will need to edit the BookBuildOverrides section of the book-build INI file that you are using with DITA-FMx. Here are the three keys that need to be edited:

[BookBuildOverrides]
...
RunScript=1
ScriptName=fsl
ScriptArgs=myEventScript

RunScript is a 0 or 1 value. Setting it to a 1 tells DITA-FMx that you want run one or more scripts or FDK clients. ScriptName for FrameScript is fsl. The ScriptArgs value is the name of the installed FrameScript “event script” that you want to run.

Before we go further, let me give a little background on FrameScript scripts. FrameScript has two kinds of scripts: Standard scripts and Event scripts. A standard script can consist of functions, subroutines, and events, but it always has an entry point that is not inside of a function, subroutine, etc. Typically, you “run” a Standard script, it loads into memory, runs to completion, then is flushed from memory.

Event scripts are not run directly; they are “installed” first and then “wait” for some kind of event to happen; for example, a menu command, a FrameMaker event, an outside call, etc. All of the code in an event script must be inside of a function, subroutine, or event. The entry point for an event script is some kind of an event inside of the script. One point that is pertinent to this post is that an installed Event script has a name, and this name is the value you use for the ScriptArgs key.

Instead of installing your event script manually, it is best to install it automatically with an Initial Script, which runs automatically whenever you start FrameMaker. That way, your event script will be installed automatically when you start FrameMaker. Here is an example Initial Script:

// InitialScript.fsl Version 0.1b August 26, 2013

Local sPath('');

// Get the path to this script.
Set sPath = eSys.ExtractPathName{ThisScript};

// Install the event script that will receive the DITA-FMx call.
Install Script File(sPath+'Script1.fsl') Name('myEventScript');

This command will install the script “Script1.fsl” that is in the same folder as the Initial Script (InitialScript.fsl). The important parameter on the Install Script command is Name; the name supplied must match the name you give to the ScriptArgs key in DITA-FMx’s book-build INI file. Here we are using myEventScript.

To run this script automatically whenever FrameMaker starts, choose FrameScript > Options and click the Browse button next to the Initial Script Name field. Navigate to the InitialScript.fsl file and select it, and then click Save to close the Options dialog box.

Before you quit and restart FrameMaker, you will need to have Script1.fsl in the same folder as the InitialScript.fsl file. Here is a shell you can use for this script:

// Script1.fsl Version 0.1b August 26, 2013

Event NoteClientCall
//	
If ActiveBook
  Set iResult = ProcessBook{ActiveBook};
EndIf
//
EndEvent

Function ProcessBook oBook
//
//... Do something with the book here ...
//
EndFunc

The NoteClientCall event is a built-in event that “waits” for the outside call; in this case, from DITA-FMx. We test to make sure that there is an active book, which should be the book that DITA-FMx just created from the ditamap. If there is an active book, we call the ProcessBook function, which is where we process our book with FrameScript code. We could have our book code right in the ProcessBook function, or we could use this function to call other scripts or functions.

Please let me [Rick] know if you have any questions or comments about calling FrameScript scripts with DITA-FMx.


Thanks Rick! I’m sure that this article will be a big help to those who want to refine the DITA-FMx ditamap to PDF publishing process with FrameScript. Do contact Rick Quatro at FrameAutomation.com with any FrameScript questions or scripting needs.


Custom scripting in the DITA-FMx book-build process

One of the popular features in DITA-FMx is its ability to generate a PDF-ready FrameMaker book and files from a DITA map. The idea is that these files are actually PDF-ready .. one click to build the book, quick review (optional), then save to PDF. Because you can define all of the setup and publishing properties in a book-build INI (ditafmx-bookbuild.ini to be precise) that is saved with each book file, you’re able to reliably and accurately regenerate the FM files for each DITA map without worrying about getting the settings right each time. This saves time and ensures accuracy and consistency.

One feature of the book-build process is the option to include custom processing in the publishing process. DITA-FMx provides many useful publishing features, but there’s always the need to perform some additional processing to achieve formatting that is specific to your needs.

The Run Custom Script option provides the ability to run one or more FDK client (plugin) command, ExtendScript, or FrameScript, at the end of the default book-build process so you can perform whatever additional processing you need. This eliminates the need to make any manual tweaks to the generated files and ensures that your output always looks the same.

If you want the same script to run for all book-builds, you can set this option through the DITA-FMx > DITA Options dialog. In the Options dialog choose the Book Builds button and on the Book Build Settings dialog you’ll see the Run Custom Script option.

fmx-custom-script

If you’re running an ExtendScript, enter “ScriptingSupport” in the Client field, and the full path (using the forward slash as directory delimiter) to the script file name in the Args field (for FrameScript, use “fsl” in the Client field).

Yes .. that’s the easy part. I suppose you want to know how to create a script?

Here’s an example of an ExtendScript that applies a background color to all elements in your files that have the @status attribute set to “changed” or “new”. You might not want to use this for the final PDF, but it’s a nice way to flag new and changed content for your reviewers.

Just copy this code to a new file named tag-changes.jsx (or whatever you’d like), and place that file in a known location on your file system. It doesn’t really matter where you put it, you just need the full path and file name  for the “Args” field in the Run Custom Script settings. (You can download the file from here .. tag-changes.jsx.)

/* 
 * ExtendScript to set a background color on elements 
 * with @status='changed' or @status='new' in all 
 * files in book 
 *
 */
Console("Running ExtendScript on book."); 
// get the active book
var book = app.ActiveBook;
// get the first component in that book
var comp = book.FirstComponentInBook;
// iterate over all components
while (comp.id) 
{
  // get the component name (file name)
  var compName = comp.Name;
  // open that file (ok if already open)
  var doc = SimpleOpen (compName, false);
  // print a message to the console
  Console("Processing: "+compName); 
  // call our function to perform the processing
  fnProcessDoc(doc);
  // get the next component and loop back
  comp = comp.NextComponentInBook;
}

// function that starts the processing of a document
function fnProcessDoc(doc)
{
  // get the root element in the file
  var rootElem = doc.MainFlowInDoc.HighestLevelElement;
  // run function to process all elements in the file 
  // .. (if it's structured)
  if (rootElem.id) {
    fnProcessElements(doc, rootElem);
  }
}

// iterative function to walk through all elements 
function fnProcessElements(doc, elem)
{
  // get the value of the status attribute
  var statusVal = fnGetAttributeValue(elem, "status");
  // check if value is "new" or "changed"
  if ((statusVal == "new") || 
      (statusVal == "changed")) 
  {
    // set the background color to "Green"
    fnSetBackgroundColor(doc, elem, "Green") ;
  }
  // scan for more elements
  var child = elem.FirstChildElement;
  while (child.id) {
    fnProcessElements(doc, child);
    child = child.NextSiblingElement;
  }
}

// function to get the specified attribute value
function fnGetAttributeValue(elem, attrName)
{
  var attrVal = "";
  var attrs = elem.GetAttributes();
  for (n=0; n<attrs.len; n++) {
    if (attrs[n].name == attrName) {
      attrVal = attrs[n].values;
      break;
    }
  }
  return attrVal;
}

// function to set the background color
function fnSetBackgroundColor(doc, elem, colorName) 
{
  var color = doc.GetNamedColor(colorName);
  // check that the color is valid
  if (color.ObjectValid()) {
    var setVal = new TypedVal();
    setVal.valType = Constants.FT_Integer;
    setVal.ival = true;
    doc.SetTextVal(elem.TextRange, 
        Constants.FP_UseBkColor, setVal);
    setVal.valType = Constants.FT_Id;
    setVal.obj = color;
    doc.SetTextVal(elem.TextRange, 
        Constants.FP_BkColor, setVal);
  }
  else {
    Err("Invalid color: " + colorName + "\n");
  }
}

If you’re using a book-build INI file, you can add these settings in the BookBuildOverrides section:

[BookBuildOverrides]
...
RunScript=1
ScriptName=ScriptingSupport
ScriptArgs=C:/publishing/scripts/tag-changes.jsx

When you run the DITA-FMx > Generate Book from Map command, this script will run and your files will be processed accordingly.

If you want to run multiple scripts (or different types of scripts, ExtendScript + FDK plugin), you just use the vertical bar as a delimiter in the ScriptName and ScriptArgs values (be sure to start with a vertical bar). The following example shows how to run the tag-changes script followed by the StructureSnippets plugin.

[BookBuildOverrides]
...
RunScript=1
ScriptName=| ScriptingSupport | Pubs-Tools:StructureSnippets
ScriptArgs=| C:/scripts/tag-changes.jsx | RUNSNIPPETSCRIPT

For FDK plugins, the ScriptName is the “client name” as defined in the maker.ini file, and the ScriptArgs value will vary depending on how that plugin was set up.

This Run Custom Script feature makes it so you can tailor the way FM files are generated from a DITA map to exactly suit your needs. Don’t let anyone tell you that PDFs from DITA have to be boring or ugly!