Friday, October 10, 2008

Coping with Conditions in Framemaker (Or, "How I Learned to Stop Worrying and Automate my Conditions in FrameMaker.")

I have a problem with my docs. In fact, I have many many problems with my docs...but in the interests of brevity (this statement will certainly be a joke by the time we are done), I'm going to limit this article to just one of the problems with my docs: how I cope with an absurd amount of conditions in FrameMaker.

Before we get into the nitty gritty, however, I'll cover a few salient details.
  • I use FrameMaker 6.x for all my docs. Yes, I know it is old. Yes, I know more recent versions of FrameMaker have dramatically improved conditions... but some time ago I was told that I was not allowed to upgrade to a more recent version of FrameMaker. Rather than fight that system, I set out to find a better way of coping with conditions in FrameMaker 6.x.
  • I both single source and shared source a sizable percentage of content. This is the only reasonable way for one person (me!) to even give the appearance of being able to manage, update, and work in so many documents across so many product lines. Because the terms "single source" and "shared source" are not always used consistently, I'll digress for a moment to define them in the context of this article:
  • Single-sourcing: Using one source document for producing
    output in different formats. Of course in my case, we are
    talking about hundreds of source files, any one of which contains
    content used in both PDF and WebHelp output.

  • Shared-sourcing: Using one source document for producing output for different product documents with overlapping features and functionality. As in the case of "single-sourcing" above, I am really talking about hundreds of source files here, any one of which can contain content shared amongst multiple product lines.
The only way that it is possible for us (and by "us", I really mean "me") to manage this amount of documentation is by extensive use of conditional text. It quickly gets out of hand.

To avoid identifying the guilty, let's explain the problem by hypothetical example. Let's say I work for Widget Co, and we make all sorts of widgets. Our documentation deliverables for each widget consists of a mix of PDF and Online Help files. Furthermore, though each type of widget we sell shares a core amount of standard functionality, each also has a unique set of features and functionality. Based on this circumstance, I have a plethora of conditions in my source documents. I think I'm approaching 60 distinct ones. Maybe more. All sorts of crazy algebra goes into it. Unique conditions for Product A online, Product A PDF, Product B online, Product B PDF, Product A if used in conjunction with Product B (online and and PDF), etc etc etc. Believe me, the number of conditions grows very fast in my situation...but it works. Sort of.

The weak point in this approach is the author. It was simply taking me too long to apply and change all the conditions when I had to edit or write new content. Sure, the ol' CTRL+4 trick allows you to "quickly" apply conditions in FrameMaker...but in this case, "quickly" is a relative word, and after a week of doing it that way, my hands were contorted into claws from the repetition and pushing myself to do it faster and faster.

A further complication arose out of the sheer number of conditions I used: because we have so many conditions, I had to make the names of the conditions meaningful so that after a 3 month hiatus on a given document, I could look at the condition names and figure out what they were intended to do and how they were to be used. This meant that my condition names were long and descriptive. This, in turn, meant that it took a long time to apply a condition to a piece of text simply because of the length of the condition name I had to type. Believe me, I tried using short names and keeping a reference index that explained what each condition did...but that quickly resulted in a huge mess of incomprehensible condition tags and I was spending a long time checking and double-checking what condition "A1" was supposed to do in comparison to conditions "A2-A45". You probably catch my drift...

So, I went back to finding a method that allowed me to keep my condition names self-explanatory, yet would not impose a time burden on the author (me!) when applying them. Enter XKeys and AutoIT.

XKeys
XKeys is a special type of "macro keyboard" produced by P.I. Engineering. It's basically a blank keyboard with no labels on the keys. You can program the keys to do anything you want using the scripting software that comes with the keyboard. In fact, you can even assign a given key to a specific .exe file so that the file is executed when the key is pressed. PIEngineering (the makers of the XKeys keyboards) sends some nifty stickers that you can use to label each key. Scroll down a bit for an illustration of the XKeys keyboard that I use.

AutoIT
AutoIT is a Visual Basic-like scripting language that allows you to automate interaction with Windows and Windows software. It's free, and pretty powerful (if you can get over the stigma of working with something like Visual Basic in a software engineering group) for automating things in the Windows environment.
From the AutoIT website:
AutoIt v3 is a freeware BASIC-like scripting language designed for automating the Windows GUI and general scripting. It uses a combination of simulated keystrokes, mouse movement and window/control manipulation in order to automate tasks in a way not possible or reliable with other languages (e.g. VBScript and SendKeys). AutoIt is also very small, self-contained and will run on all versions of Windows out-of-the-box with no annoying "runtimes" required!

AutoIt was initially designed for PC "roll out" situations to reliably automate and configure thousands of PCs. Over time it has become a powerful language that supports complex expressions, user functions, loops and everything else that veteran scripters would expect.
XKeys + AutoIT = Nirvana (or at least a tangible improvement)
Once I got my XKeys keyboard I set about trying to make it useful. After poking around with their scripting interface a bit, I muttered a few choice expletives and gave up on it. Don't get me wrong, their scripting language and interface (MacroKeys) may be the cat's meow, and will allow you to script a lot of things without having to "program", but it just seemed to keep getting in my way. Perhaps it was because I wanted to approach the problem programmatically, but I didn't have the time budget to really dive in and learn MacroKeys. After all, I was already fairly confident AutoIT could do what I wanted, and I had a rough idea of the code it would take to do it. I just had to figure out how to tie the two together.

The solution is patently obvious, but it took me some trial and error. (Hey, I'm not a programmer, engineer, or one to quickly apprehend the obvious in most circumstances.) The trick was to write my script in AutoIT, compile it to an .exe file, and assign the .exe file to one of the keys on the XKeys keyboard. There's one more thing I wanted to do: I had to make sure that the script would only run when FrameMaker was active. After all, I didn't want to, for example, send a stream of keypresses to PhotoShop if I pressed an XKeys key accidentally. Since I knew that this was easy to do in AutoIT, I set about to write my first AutoIT script to apply a specifc condition to a FrameMaker document. Here's what the AutoIT code looks like for one condition (feel free to copy and paste into AutoIT to try it out. Mind you, you'll have to change the name of the actual condition used in the macro to suit your needs.):

#include-once
#cs ------------------------------------------------------------------
AutoIt Version: 3.1.1.0
Author:         michael
Script Function:
Template AutoIt script.

Script Name:
     WWHiPlash

Version:
     0.11
#ce -----------------------------------------------------------------------
#region --- Standard initialization stuff ---
#include

#region --- ScriptWriter generated code Start ---
Opt("WinWaitDelay",100)
Opt("WinTitleMatchMode",4)
Opt("WinDetectHiddenText",1)
Opt("MouseCoordMode",0)
#endregion --- Standard initialization stuff ---


If WinActive("Adobe FrameMaker+SGML") Then
; (above)Macro will only continue executing if FrameMaker is currently
; active.  The text within quotes will have to be changed
; to match your current version of FrameMaker.  (Above is
; for FrameMaker 6 + SGML.  Look in the FrameMaker Window blue-bar or use
; the AU3Info tool that comes with AutoIT to see what text
; you need.
     Send("{CTRLDOWN}")
     Send("4")
     Send("{CTRLUP}")
     Send("some_condition_name") 
       ; Text within quotes is the name of the condition    
       ; that will be applied.  Change it to fit your    
       ; needs.     
       Send("{ENTER}")
EndIf


Mind you, you can try out this AutoIT code without actually having an XKeys keyboard...you'll just have to run the .exe file manually. The problem with this is that it is likely that it won't do anything because as written, the script checks to make sure that FrameMaker is currently active. If it isn't, it won't run. So if you want to try out the script without an XKeys keyboard, you can tweak the AutoIT code to automatically activate the FrameMaker window before running the rest of the macro. The code would look like this:


#include-once
#cs ------------------------------------------------------------------
AutoIt Version: 3.1.1.0
Author:         michael
Script Function:
Template AutoIt script.

Script Name:
     WWHiPlash

Version:
     0.11
#ce -----------------------------------------------------------------------
#region --- Standard initialization stuff ---
#include

#region --- ScriptWriter generated code Start ---
Opt("WinWaitDelay",100)
Opt("WinTitleMatchMode",4)
Opt("WinDetectHiddenText",1)
Opt("MouseCoordMode",0)
#endregion --- Standard initialization stuff ---


WinActivate("Adobe FrameMaker+SGML")
WinWaitActive("Adobe FrameMaker+SGML")
If WinActive("Adobe FrameMaker+SGML") Then
; (above)Macro will only continue executing if FrameMaker is currently
; active.  The text within quotes will have to be changed
; to match your current version of FrameMaker.  (Above is
; for FrameMaker 6 + SGML.  Look in the FrameMaker Window blue-bar or use
; the AU3Info tool that comes with AutoIT to see what text
; you need.
     Send("{CTRLDOWN}")
     Send("4")
     Send("{CTRLUP}")
     Send("some_condition_name") 
       ; Text within quotes is the name of the condition    
       ; that will be applied.  Change it to fit your    
       ; needs.     
       Send("{ENTER}")
EndIf


Implementation

Once I got the first condition working, the rest was easy:

  1. Make a new script in AutoIT for each of my remaining conditions.
  2. Compile each script to an .exe file
  3. Assign each script executable to the appropriate XKeys keyboard key.
You can see the assignment of all my condition macros to the XKeys keyboard in the image to the right. Note that this screenshot is of the XKeys programming interface. To assign an executable file to a particular key, you just drag the file from Windows Explorer and drop it on the key of your choice. It doesn't get much simpler than that.

Screencast
If you are having trouble understanding how effective using an XKeys keyboard with AutoIT can be, I made the following screencast showing how I can quickly manipulate a lot of conditions quickly.



Unfortunately, the video doesn't quite do the speed justice. I don't know whether I just had too many applications open or what, but my system was performing sluggishly all morning and the screencast recording software didn't improve things. Still, the video should give you a rough idea of what I'm talking about. When you see conditions being applied in the video, keep in mind that they are being applied and cleared by pressing specific keys on the XKeys keyboard.


Resources
  • Downloading AutoIT: Follow the link to the download page for AutoIT. I recommend that you download both the "AutoIT Full Installation" package and the additional "AutoIT Script Editor". The AutoIT Script Editor is a customized version of SciTE that provides a lot of extra features for simplifying scripting in AutoIT.
  • Purchasing XKeys: Follow the link to P.I. Engineering's XKeys product pages. Search for the model you want, and follow the purchase instructions. Though XKeys products are apparently available in several retailers, I ordered mine through the website and recieved it a few days later via UPS. Specific purchasing instructions are available on this page.

3 comments:

Anonymous said...

Hey, looks like you were picked up by MicroType. Congrats...

-michael said...

Hey Anon. Thanks! MicroType is a cool resource, and they seem to cover everything Frame related.

Framemaker Scripting Victim said...

Oy! I went through a bunch of contortions around this once upon a time. I had some FDK stuff I'd written that could automate some operations, but Framemaker itself, with lots of conditions, seemed to get slower and slower... had about 100 conditions, and 70 combinations of them that actually had to be generated. From hell. In the end I wound up exporting the doc to MIF, filtering out the bits I wanted there using a bunch of Perl code, then converting the MIF back to Framemaker... And this was Structured Framemaker/XML too. Boy, am I glad I don't have THAT job any more. (Worked with some good people, but the tasks... oy.)