Removing Conditions From Text and Table Rows – Part 4

Before continuing our discussion of removing conditions from text, let’s take a look at table rows. Working with conditional table rows is easier that working with conditionalized text, so we can get this code out of the way. To set the stage, add a table to a document and apply two conditions, Condition1 and Condition2 to one of the rows. Select the conditionalized row and run this code:

Condition Formats applied to the row

This is similar to what we did when working with a text range in Part 1, but more direct. Since we have a function for removing conditions from text, we can use this as a starting point for a table rows function.

Let’s rework it together line-by-line: on line 1, change the name to removeConditionsFromRow and change the first argument from textRange to row.

We won’t need a prop variable so remove it from line 3:

Lines 5-10 can be shortened to this:

Lines 12-19 from the original function can be used as is, except for a minor change to the first comment:

Finally, lines 21-25 of the original function can be changed to work with a row:

Now we can test the finished function. Add the following code before the removeConditionsFromRow function and add the augmentObjects function to the end of the script. Select the table row that has Condition1 and Condition2 applied to it and run the complete script. You should see the Condition2 format removed from the selected row.

In an upcoming post, we will return to working removing conditions from text.

Easy Authoring In – Structured Content Out

Some of my work involves clients that submit content from authors and editors (produced on word processors like MS Word) that needs to be formatted for high-quality print output. I may be using InDesign or FrameMaker for the print output, but the process is pretty much the same: import the content into the page layout program and apply styles, etc. This can be a tedious, error-prone process, compounded by edits that happen after the pagination process has begun.

One possible solution that appeals to me is a light-weight editor that could be configured for content-specific authoring and editing. It would allow you to specify an underlying schema so you could control and guide the development of content. But it would also have a simple editing environment so that it would be easy to add and format content without messing with the underlying XML. The editor could have ways to insert commonly used items that conform to the required structure. And if such an environment was browser-based, it could make collaboration easier. With solid XML, I could then use XSLT to machine-convert the content to InDesign or FrameMaker for high-quality output.

I recently saw a demo for a product that may finally be the solution I have been looking for: FontoXML. First a disclosure: I am not in any way officially associated with them. But I was very impressed with what I saw. They developed FontoXML as a custom solution for several of their clients, but it was so well received that they decided to make a general-purpose product for others. I only saw an hour-long demo so I am not going to try to provide a detailed review, but here are some of the things I liked:

  • It was developed with a modular architecture that lends itself to customization and extension.
  • It can work with other schemas besides DITA, including custom schemas.
  • The authoring environment can be customized through HTML/CSS.
  • Placeholders and boilerplate content can be developed to streamline the authoring process.
  • It can be integrated with content management systems.
  • The demo was very polished and showed a high attention to detail.
  • The prices were very reasonable, even for small-volume clients.

I am sure there are similar products out there that I don’t know about. If you know of any, please feel free to leave a comment below. But, I would highly-recommend that you take a look at FontoXML. It is expected to ship 1st Q 2015.

Removing Conditions From Text and Table Rows – Part 3

Part 1 and Part 2 of this series show the beginning of the typical script development process that I use. I like to isolate scripting tasks to the smallest units possible. For this task, we worked on a single selection of conditionalized text in the active document. When we switched to tables, we selected a single row with conditions applied. This approach allows us to get the basic functionality working without worrying about the overhead that the larger script requires. Once the basic functionality is working, we can expand it to work on an entire document or book.

Let’s start by figuring out how to specify which Condition Formats to remove from the text and table rows in a document. To keep things simple, we can use an array of Condition Format names:

Even if we add an interface to the script later (like a dialog box), we can still use an array to store the condition names. We need to figure out a way to compare the contents of the array with the conditions that are applied to a given range of text. To set this up, make sure you have a document open with two Condition Formats, Condition1 and Condition2, applied to a paragraph. Select some of the text in the paragraph. We can add our array to some of the code we previously developed:

The script will display each condition format that is applied to the selected text. We need a way to test each name and see if it is in the removeConditions array. Any conditions that are in the removeConditions array need to be removed from the condFmts list. In some implementations, a JavaScript array has an indexOf method so you can test and see if the array contains a particular member. If the member exists, indexOf returns the array index; otherwise, it returns -1. We could do this:

The indexOf method is being used in the if statement on line 12. There is one problem with this code: The current Adobe ExtendScript implementation does not have an indexOf method on arrays. We are going to call (line 4 in the following code) a function (lines 27-45) that will augment the built-in Array object and add an indexOf method to it. Object augmentation is beyond the scope of this lesson, but google it if you are interested in how it works. Here is the entire code:

OK, we have the basic functionality working, so let’s encapsulate it into a function that we easily drop into a bigger script. We want the function to take a text range, a list of conditions to remove, and a document object. Here is one way we can do this:

Some important points:

  • The new function is on lines 14-34.
  • We followed best practices and declare all variables at the top of the function (line 16).
  • Because the function uses the indexOf method, we have to include the augmentObjects function in any script that uses our new function. In addition, we have to remember to call augmentObjects at the top of our script (like we do on line 4).
  • IMPORTANT: The script gets the condition format properties at the beginning of the text range. It removes the appropriate conditions from that list and then applies those properties to the entire text range. For this reason, we want to pass in text ranges that only have distinct condition formats applied to them. If you are not sure why, post a comment below.

Before moving on, there is one issue we should address. One line 32, we apply the updated list of conditions back to the propVal object; then line 33 applies the property list to the text range. But what if this list was empty to start with, or hasn’t been changed by the loop on lines 23-29? There is no sense in going further, so let’s add a couple of tests to the function. We can add this below line 20:

If there are no conditions applied to the text, then there are none that can be removed; so we simply exit the function. If there are conditions applied, they may be conditions that aren’t in our removeConditions array. So we can test to make sure that conditions were actually removed before updating the text (this code would replace lines 31-33):

Here is the finished function:

In a future post, we will show how to loop through the text in a paragraph and isolate ranges of text that have distinct sets of conditions applied to them. These will get passed to our new removeConditionsFromText function to remove the specified conditions from paragraphs in a document. We will also develop a function that will remove conditions from table rows.

Removing Conditions From Text and Table Rows – Part 2

In the previous post, we gained a basic understanding of how to remove a CondFmt (Condition Format) from a text range. Let’s see it in action. To set this up, make sure you have a sample document with two conditions, Condition1 and Condition2, and have both conditions applied to a paragraph in the document. Select some of the text in the paragraph and run this:

To keep the example simple, we are making a couple of assumptions: First, we are removing Condition2 from the text and second, that Condition2 is the second CondFmt in the osval list. We will use code to eliminate these assumptions later.

On line 7, we assign the osval list to a variable (condFmts). We need a way to remove members from this condFmts list. If condFmts was a true JavaScript array, we could use its splice method to remove the desired member. But it is actually an array-like list of CondFmt objects that doesn’t contain a splice method. However, JavaScript allows us to “borrow” the splice method from the built-in Array object and use it on our list; this is what we are doing on line 10.

On line 11, we assign the modified list back to prop.propVal.osval and on line 14 we apply the updated property to the selected text. When you run this, you should see the Condition2 format removed from the selected text.

If we wanted to remove all formats from the text, we could use this shortcut:

Line 9 simply sets the length of the osval list to 0 (zero), which removes all of the members.

Conditionalized table rows are handled a little bit differently than text. To set this up, add a table to your document and apply Condition1 and Condition2 to one of the rows and leave the row selected. Run this code:

As you can see, the condition list is easier to get to with a table row than it is with text. Let’s splice the second member from the list and apply it back to the table row:

As with text, we can use a shortcut to remove all conditions from the row:

In the next post, we will generalize this code into functions so that you can remove conditions from all of the text and table rows in a document.

Removing Conditions From Text and Table Rows

It is easy to delete Condition Formats from a FrameMaker document in the interface. If you try to delete a Condition Format that is use, you will be prompted on what to do to the conditionalized text:


With ExtendScript, things aren’t so easy. If you delete a Condition Format programmatically, the conditionalized content is always deleted along with the format. There is no “option” on the Delete function to make the text unconditional. If you want to keep the text, you will have to remove the Condition Format from any text (and table rows) where it is applied.

A couple of preliminary things: First of all, you should show all of the conditions at the beginning of your code. You can store the current ShowAll setting so you can restore it later. Second, if you do want to delete the text and rows with the Condition Format, just delete the Condition Format (CondFmt):

To see how to remove Condition Formats from text, make a sample document and add two Condition Formats to it, Condition1 and Condition2. Apply both conditions to a single paragraph. Select part of the paragraph and run this code:

This will show us a PropVal object that has information about the Condition Formats (if any) that are applied to the text. Run the following code to see the properties of the PropVal object.

The propIndent value is the kind of property it is (FP_InCond), while the propVal properties has specific information about the Condition Formats that are applied. Let’s poke further down and look at the propVal properties with this code:

As you can see, the propVal property has a lot of child properties. For Condition Formats, we are interested in the osval property. Make sure your conditional text is still selected and run this code:

You should see a dialog box like this:


The osval property is an array-like list of the CondFmt objects that are applied to the text. If there is no CondFmts applied, this osval list will be empty. Because this list is similar to an array, we can loop through it to look at the individual CondFmt objects.

When you run this code, you should see the name of each CondFmt displayed in a dialog box. You can probably see what we need to do here. Assuming that we want to remove Condition2 from the text, we need to figure out how to remove the Condition2 CondFmt object from the osval list. Once we do that, we can take the modified PropVal and apply it back to the text, which will remove the unwanted Condition Format. We will explore that in Part 2.

Regular Expressions in FrameMaker

FrameMaker 12 adds regular expressions support in the Find/Change panel. To use it, select the Regular Expressions radio button as shown in the screenshot.


I had the privilege of presenting an Adobe webinar on regular expressions on November 20, 2014. The webinar was well-attended and nearly all attendees stayed until the end. If you attended, I urge you to take what you learned and go further so you confidently use regular expressions in FrameMaker.

If you have any questions from the webinar, or if you didn’t attend and have questions about regular expressions in FrameMaker, please post them in the Comments section below. It would be great to have some “Find/Change” challenges that we could attempt to solve with regular expressions. I look forward to hearing from you!

Run a Script in Response to a Command

Here is a question from the Adobe FrameMaker ExtendScript forum: “I wonder if there is there a way to make [a] script event-driven, so that it will be triggered after importing formats from another file?” This post describes how to do this with an ExtendScript script.

First, some terminology: When something happens in FrameMaker, it is called an event. You want your script to listen for one or more events to occur. In ExtendScript, you want to tell FrameMaker to notify to you when one or more events has occurred.

You can set this up in your script with a couple of functions. You first have to tell the script which events you want it to listen for.

Here, we are telling FrameMaker to notify us after a book is updated. This is sometimes referred to as registering the event. You can register as many events as you want in this function.

NOTE: There are no specific notifications when importing formats, but we will show you how to handle this shortly.

You will call this function at the beginning of your script.

Now you need a function that will listen for the notifications (or events) that you specified. FrameMaker ExtendScript has a built-in function for this:

The Notify function will receive four arguments:

  • note: This is the event that was triggered. We usually use a JavaScript switch statement to test for the event, because we may want to test for more than one event.
  • object: This is the relevant object that was in play when the event occurred. In the above example, this will be the Book object of the book that was updated.
  • sparam: This will be a relevant string value or null, depending on the event.
  • iparam: This will be a relevant integer value or null, depending on the event.

Once you receive the correct notification in the Notify function, you can call the appropriate code in your script.

Let’s go back to the forum question. FrameMaker ExtendScript has a bunch of built-in notifications, many of them in a “Pre” and “Post” format. For example, FA_Note_PreGenerate is triggered just before a book is updated and FA_Note_PostGenerate happens just after a book is updated. However, there are no specific notifications that occur before or after importing formats. To handle this, we can use the generic FA_Note_PostFunction notification, which gets triggered after nearly every FrameMaker event. When this notification calls the Notify function, the iparam value will be the unique number of the function. To find the number for a particular command, run the following code, then run the desired command in FrameMaker.

After running the script and importing formats, you should see the number 790 displayed in the alert dialog box. You probably saw a lot of other numbers if you performed other FrameMaker operations before importing formats! Now that you have the number you need, uninstall the script above or quit and restart FrameMaker.

Now we can go back and edit the Notify function that we added to our script. We will test for the Import Formats command by using the command number.

There are a couple of things to note about this particular example. If you cancel the Import Formats dialog box, the event is still triggered and the Notify event is called. If you go ahead with the Import Formats command, the Notify event is actually called twice. At this point, I am not sure how to work around this. In any case, always test Notify event carefully to ensure that your code gets called exactly when it is needed.

Naming Variables and Functions in Scripts

A friend recently asked me about how I name variables in my FrameScript scripts. I used to use a “v” prefix on all of my variables names. For example, for a paragraph I would use:

The “v” prefix would simply indicate that I was setting a variable. This is the convention I used in my 2003 FrameScript book FrameScript: A Crash Course. Later, I decided to make the prefixes reflect the data type that the variable represents. I replaced the “v” with another letter indicating the data type. For example,

The “o” prefix means “object” since both lines set variables for FrameMaker objects. I use “s” for strings, “i” for integers, “r” for real numbers, “m” for metric values, etc. These prefixes help me see at a glance what kind of data type the variable represents. This is important when I am looking at a script that I may not have worked on in awhile. Prefixes like this are also helpful when you are reusing functions. For example, here is a function that applies a named paragraph format to a paragraph.

Even if I don’t know exactly how this function works, I can see at a glance that it takes three parameters: a paragraph object (oPgf), a string indicating the paragraph format name (sName), and a document object (oDoc). The prefixes help me to quickly see the data types of each parameter.

As far as the variable name itself, I try to use the object name that the variable represents. In the ApplyPgfFmt function, I use the Get Object command to get a PgfFmt object. So, I use oPgfFmt as the variable name. Using this convention lets me quickly see that oPgfFmt represents a PgfFmt object. The sName parameter gets matched up with Name in the Get Object command and so on. I find that this method helped me memorize the FrameMaker object model because I have closely associated my variable names with the built-in FrameMaker object names.

When naming functions, the common sense approach is to use some kind of a verb form that describes what the function does. When you look at the function name and its parameters, it should be evident what the function does without looking at the actual function code.

With ExtendScript, I do things a little bit different. I don’t use the prefixes, I simply use the object name but with a lowercase first letter. Here is the example I showed earlier:

ExtendScript (JavaScript) is a case sensitive language, so pgf is different from Pgf. For function names (and longer variable names) I use the “camel case” convention. I don’t use the datatype prefixes, mainly because this is not a normal convention for JavaScript programmers. I suppose it is a bit vain, but I don’t want my ExtendScript code to look too out-of-the-ordinary. Here is the ExtendScript version of the ApplyPgfFmt function:

Running a FrameScript Script from DITA-FMx

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:

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 installed automatically when you start FrameMaker. Here is an example Initial Script:

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.

FrameScript 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:

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 know if you have any questions or comments about calling FrameScript scripts with DITA-FMx.

Riding in Memory of Bruce Foster

Bruce Foster, creator of many fine FrameMaker plugins, passed away suddenly on Saturday, June 11, 2011. Although Bruce had been diagnosed with multiple myeloma in September 2010, his death still came earlier than was expected. Bruce’s plugins for FrameMaker were BookInfo, ImpGraph, MifSave, MifToFM, RtfSave, and his most famous, Archive.

My son, Jason, and I will be riding the 2013 Ride for Roswell to benefit cancer research. This year we will ride the 104 mile route. I will be riding in memory of Bruce and Jason will be riding in memory of his grandfather, Dr. Richard J. Quatro. We would appreciate your sponsorship.

If you donate $40 or more to either of us, I will give you a copy of the ExtendScript version of PageLabeler or ImportFormatsSpecial. Both of these have been written in ExtendScript for use in FrameMaker 10 and higher. To see the documentation for these scripts, click the links above. For a $75 donation, you will receive both scripts, including the source code.

If you are using FrameMaker 9 or below, I will send you the plugin versions instead. Be sure to email me to tell me what you would like to receive after donating.

Rick has met his fundraising goal. Please click here to donate to Jason’s ride.

Thank you very much for your support!