Thursday, April 5, 2012

Working with Structured Documents

When working with a structured document, you can choose to navigate the document using the tree of elements. Or, if it is more convenient, you can work with the document as if it does not have structure. That is, you can work with paragraphs in the main flow exactly as you would in unstructured FrameMaker.

Is there a downside to this approach? The answer is, it depends. If you are cutting and pasting or otherwise adding content, working outside the element structure may lead you to make changes that leave the structure invalid. But if you are simply inspecting the document or you are working with content that is not part of the element structure (e.g.. marker content), feel free to do whatever is easiest.

Selecting an Entire Element

Selecting elements is tricky business. You will find helpful information in the FDK Programmer's Guide. I happen to have the one for version 6 on hand. In that version, you should refer to pages 366-7.

The key idea is to create an ElementRange that refers to the appropriate selection and then set the document's element selection to be that range. The following function sets the specified document's element selection to be the element passed in.

Element ranges have a beginning and an end. They consist of parent, child, and offset information. 

The parent element is the containing element for both the beginning and end of the range. The starting child is the element itself. The ending child is the element's next sibling element. As the whole element is being selected, both offsets are zero.

//Selects the specified element in the specified document
function setElementSelection(doc, element) {
        var eRange = new ElementRange;
        eRange.beg.parent = element.ParentElement;
        eRange.beg.child = element;
        eRange.beg.offset = 0;
        eRange.end.parent = element.ParentElement;
        eRange.end.child = element.NextSiblingElement;
        eRange.end.offset = 0;
        doc.ElementSelection = eRange; 

Wednesday, March 14, 2012

Opening Files Off Screen

If you are processing a large number of files without user interaction, consider opening those files off screen. This allow your script to work faster (as there is no need to update the display) and to do its work without screen fireworks.

Use Open() and set the open parameter Constants.FS_MakeVisible to False to open a file off screen. Files opened this way can be modified in exactly the same way as files that are on screen.

If you work with files in this way, keep in mind is that your script must take total responsibility for managing off screen files. When a script is done with an off screen file, it must save (if changes are to be retained) and close the file. Failing to do so leaves the file locked and open but inaccessible to an end user.

There may be a case where you want to open a file off screen, work with that file, and then make it visible to users. To do so, set the document property, FS_MakeVisible to True when you are ready to reveal the file to users. 

Tuesday, March 13, 2012

Opening Book Files when there are Issues

If you use SimpleOpen() to open book files non-interactively, files with missing graphics, missing fonts, or other issues will not open. Use Open() with an appropriate set of parameters to solve this problem.

The following function sets up open preferences that allow files of the following types to open without user interaction:
  • Files which reference missing files.
  • Files that are in an old FM version.
  • Files with missing font issues.
  • Files that are locked.
function getOpenPrefs() {
    var params, i;
    params = GetOpenDefaultParams();
    i = GetPropIndex(params, Constants.FS_RefFileNotFound);
    params[i].propVal.ival =
    i = GetPropIndex(params, Constants.FS_FileIsOldVersion);
    params[i].propVal.ival = Constants.FV_DoOK;
    i = GetPropIndex(params, Constants.FS_FontChangedMetric);
    params[i].propVal.ival = Constants.FV_DoOK;  
    i = GetPropIndex(params, Constants.FS_FontNotFoundInCatalog);
    params[i].propVal.ival = Constants.FV_DoOK;   
    i = GetPropIndex(params, Constants.FS_FontNotFoundInDoc);
    params[i].propVal.ival = Constants.FV_DoOK;   
    i = GetPropIndex(params, Constants.FS_LockCantBeReset);
    params[i].propVal.ival = Constants.FV_DoOK;       
    i = GetPropIndex(params, Constants.FS_FileIsInUse);
    params[i].propVal.ival = Constants.FV_OpenViewOnly;     
    return (params);
The function to open the book using these parameters is shown here:

function openBookFiles(book) {
    var doc, component, compName;
    var openParams, openReturnParams;
    openParams = getOpenPrefs ();
    openReturnParams =  new PropVals();

    component =book.FirstComponentInBook;
    while(component.ObjectValid() ){    
        compName = component.Name;
        doc = Open(compName, openParams, openReturnParams);
        component =  component.NextComponentInBook;

Monday, March 12, 2012

Saving FM Binary in Old Version

If you have ever needed to revert FrameMaker files to an older version, the following script does so automatically. It works on the active document but you could convert it to work on a book or a directory of files.

The key to the script is the Save() method which allows you to set the file type as desired. I have chosen FrameMaker 9 (FV_SaveFmtBinary90) but you can  go as far back as FrameMaker 6 (FV_SaveFmtBinary60).

I chose not to change the file name. I end the script by closing the FM 10 version of the file without saving it. The FM9 version is already on disk.

var doc,  name, saveParams, i;

doc = app.ActiveDoc;
name = doc.Name;

saveParams = GetSaveDefaultParams();
returnParams = new PropVals();
i = GetPropIndex(saveParams, Constants.FS_FileType);
saveParams[i].propVal.ival =Constants.FV_SaveFmtBinary90;
doc.Save(name, saveParams, returnParams);

After running the script, I opened the test file just to convince myself this really works.

Sunday, March 11, 2012

Using SimpleOpen() without User Interaction

SimpleOpen() can be called interactively or without user interaction. This distinction makes a difference if you are dealing with files that have issues.

If you are calling SimpleOpen() interactively, the user decides what to do when there are missing fonts, locked files, or other problematic situations.

If you call SimpleOpen() without user interaction FrameMaker defaults to very conservative behavior.
I created a simple a test to demonstrate this behavior. I cobbled together a set of files, each with a distinct issue, and put those files into a FrameMaker book. Each of my four files has one of the following issues:
  • It is locked.
  • It contains missing graphics.
  • It uses unavailable fonts.
  • It is in an older FrameMaker version.
Opening these files from the user interface brings up a dialog that allow the user to proceed or abort the open. Opening these files using the following code produces no user interaction and only one of the files opens: the locked file is opened in view only mode.

doc = SimpleOpen(compName, false);

Wednesday, February 22, 2012

Opening a File with User Interaction

The easiest way to open a file is with SimpleOpen(). This is the analog to SimpleSave() discussed here.

If you do the open interactively, you do not need to know the file's location. You simply let the user locate the proper file.

The following script passes an empty string for the fileName parameter. FrameMaker assumes the file is the last one opened.

var doc;
doc = SimpleOpen("", true);

To open a file without user interaction, you need to pass it is full pathname and false for the second parameter.