Monday, January 30, 2012

Text Item and Line Ends

Understanding exactly what text items are produced when you call GetText() is critical to writing correct scripts. I have been looking at when you get a FTI_CharPropsChange notification upon calling GetText(). In particular, I am interested in what happens at line ends.

Each of the examples below show what happens when you run a simple script that prints a set of requested text items including changes in character properties and line ends to the Console. Each file tested has one "word" consisting of the digits from 0 through 9. In each case three of the digits have been italicized using the Format>Style menu.

I was expecting to see, among other things, an indicator of where any changes in the italic properties of the text begin and end. The problem I have found occurs when the italic text is at the end of a line. In such cases, I do not get the second FTI_CharPropsChange indicator I am expecting. This behavior deviates from what I knew to be the case in the FDK in earlier versions of FrameMaker. I have not yet tried this in the FM 10 FDK.

var doc, mainflow, tItems;

doc = app.ActiveDoc;
mainflow = doc.MainFlowInDoc;
tItems = mainflow.GetText(Constants.FTI_CharPropsChange | Constants.FTI_TextObjId | Constants.FTI_LineEnd);
PrintTextItems(tItems);


Here is a case that goes as expected, yielding a change indicator before and after the italicized text.



Now look at what happens when the italic text is at the end of the line. Here I get only the initial indicator. In my view, this is acceptable as this this a special end of flow case. There is no non-italic character after the italic "9".






If a carriage return is added after the italic "9", I expect but do not get the second character properties change indicator. Note that end of paragraph symbols are selectable and this one is not italic.




Finally, lets use a soft return as our final character. Once again, there is only the beginning indicator.


I bring all this up not to be picky but because this change/bug complicates working with GetText(). If have I have missed something, please let me know.

Sunday, January 29, 2012

Customizing a Save Operation

The following script uses the Save() method to alter the format in which a file is saved. Using this method requires more code than SimpleSave() but offers a whole range of customization options. It takes three pages in the Scripting Guide to document the possibilities. Whatever changes you want to make, however, can be accomplished by following the model shown here:
  • Use GetSaveDefaultParams() to get a list of parameters whose default settings can be altered.
  • Use  new PropVals() to create an empty parameter list to be used to return information about the Save() operation.
  • Use GetPropIndex() to locate one or more of these properties of interest.
  • Reset that properties value in the parameter list.
  • Call Save() passing in the save as name, and the two parameter lists. 
The critical function is shown here:

function saveAsPdf(doc) {
    var saveParams, name, i, baseName, status, fullName;
    name = doc.Name;
    baseName = dropSuffix(name);//drops everything from last period
    fullName = baseName + ".pdf";
    saveParams = GetSaveDefaultParams();
    returnParams = new PropVals(); 
    i = GetPropIndex(saveParams, Constants.FS_FileType);
    saveParams[i].propVal.ival =Constants.FV_SaveFmtPdf;
    doc.Save(fullName, saveParams, returnParams);
    i = GetPropIndex(returnParams, Constants.FS_SaveNativeError);
    status = returnParams[i].propVal.ival;
    if (status === Constants.FE_Success) {
        return (true);
    } else {
        return (false);
    }
}

Friday, January 27, 2012

Creating a Marker (Correction to previous post)

When you create a marker you  need to specify the marker type identifier. If you are using an built-in type, use the name found in the English user interface.


Use GetNamedObject() to get this identifier. Use the appropriate marker type name. (This is a document and not a session property because of the fact that new types can be added to a given document.)

function createMarker(doc, pgf, offset, type, text) {
    var tLoc, marker, markerType;
    tLoc = new TextLoc(pgf, offset);
    marker = doc.NewAnchoredObject(Constants.FO_Marker, tLoc);
    markerType = doc.GetNamedObject(Constants.FO_MarkerType, type);
    marker.MarkerTypeId = markerType;
    marker.MarkerText = text;
    return 1;
}


You only need to  set the marker type properties for a built-in type if you want to change them. For example, add the following line of code to change the type name as displayed in the user interface.

markerType.Name = "My glossary";


Thursday, January 26, 2012

Saving a document without user interaction

The following script makes a trivial change to that presented in my previous post to save the active document with no user interaction.


var doc, name;

doc = app.ActiveDoc;
name = doc.Name;
doc.SimpleSave(name, false); //no user involvement

Sunday, January 22, 2012

Saving a document with user interaction

If your script makes changes to a document, you will want to save that file. This post, as will the next several posts, discuss the various ways you can do so.

From a scripting point of view, there are two possible ways to save a file. The first is deemed "simple" and the second is more complex to call and customizable. (In the FDK world, this option was deemed 'script-able.') The different between the two lies in the degree of configuration offered.

Simple operations use default settings but do offer one big choice. They allow you to choose whether you want to do the save operation with or without user interaction.

If you are doing a batch operation, you are not going to want user interaction. If, however, you are creating a command a user might call that needs user input, allowing a user to make choices regarding a save in the exact same way as he or she might had they invoked the command form the menu can be useful.

The following script invokes the save dialog using a script.

var doc, name;

doc = app.ActiveDoc;
name = doc.Name;
doc.SimpleSave(name, true); //true signifies user directed


Here is what happens when I saved a file using this script. It is the equivalent o the user choosing File>Save.

Wednesday, January 11, 2012

Rearranging the Tree of Elements

You can use the Cut, Copy, Paste methods to move selected elements around in the tree. There are, however, some special cases where you can take advantage of built-in tree manipulation functions.
  • Use the document method PromoteElement() to make the selected element the sibling of its former parent.
  • Use the document method DemoteElement() to make the selected element the child of its preceding sibling.

The following simple script promotes the selected element. You could easily modify it to demote an element.

var doc;

doc = app.ActiveDoc;
doc.PromoteElement();
if (FA_errno !== 0) {
    Alert(FA_errno + '');
}

The script checks for an error code:
  • Constants.FE_WrongProduct (-60) signifies that the product interface is not set to Structured FrameMaker .
  • Constants.FE_BadDocId (-2) indicates a problem with the document identifier.
  • Constants.FE_BadSelectionForOperation (-59) indicates that the action requested could not be taken on the selected element. For example, you cannot promote the root element or its children.

Here is the before and after that results from promoting the p element.
Before element promotion
After element promotion
This promote operation happens to make the structure tree invalid but the operation itself is sound.

Attempting to promote the root element produces the following error code message Constants.FE_BadSelectionForOperation (-59) as displayed by the script.


Sunday, January 8, 2012

Text Nodes

Text nodes appear in the element tree when a container element has multiple children some of which are containers for text and some of which are just a text string.

Consider the case of an element, p, that allows text (<TEXT>) and other elements as described in the EDD:



I have made a small modification to oil.xml to illustrate this point. The word "every" is italicized by wrapping it in the element i. Before this change, the p element had no children. Now it has three. The first and the last of these are text nodes as illustrated below.



The function getElementName() uses the element property ElementDef to find the definition object and then the name property to get the name string.

function getElementName(elem) {
    var elemDef, name = null;

    elemDef = elem.ElementDef;
    if (elemDef.ObjectValid()) {
        name = elemDef.Name;
    }
    return name;
}

If an element definition is NULL, that element is a text node. Armed with this knowledge, the displayAttrs() function used in a previous post can be improved.

//writes element name and list of attributes to the console
function  displayAttrs(elem) {
    var i, name, aName, attrs;
   
    name = getElementName(elem);
    if (name != null) {
        Console(name);
        attrs = elem.GetAttributes();
        for (i = 0; i < attrs.len; i++) {
            aName = attrs[i].name;
            Console( "     " + aName);      
        }
    } else {
        Console("Selected element is a text node");
     }
}

Here a text node is selected:

The output when displayAttrs() is called on the selected text node is shown here: