2 Replies Latest reply on Jun 30, 2017 8:01 AM by _tlmason

    How to Insert a Framemaker Variable Using Extendscript


      I've been fighting with this for several days and just cannot get the results I need.


      The situation: I work in technical/how-to documentation. Several older documents use a font-face to represent mouse and keyboard actions. Since it's a font, it's not accessible. In an effort to make accessible documentation, we are trying to convert the font characters to text variables. The text variables exist in the document, so it's simply a matter of finding the characters to be replaced and replacing them with the appropriate variable. Some of our documents are over 100 pages, so replacing the characters manually would be frustrating.


      Current State of Solution: I have created a script that looks through a document, then inserts the text value of the variable in the text, but for some reason, I can't figure out how to make it insert the variable object itself. I've taken scripts from around these forums and modified them to work in my situation, so here are the functions that I'm working with:


      Find and Replace function

      function FindAndReplaceEdufont(docum, flow, findString, replaceVarName) {
          var tr = new TextRange();
          var restoreTR, frame = 0;
          var loopCounter = 0, replacementCounter = 0;
          var findParams = new PropVals();
          //if the flow object is not valid, assume the main flow
          if(docum.ObjectValid() && !flow.ObjectValid()){
              flow = docum.MainFlowInDoc;
          //get the first text frame in the flow, a starting point to
          //find the first paragraph
              frame = flow.FirstTextFrameInFlow;
          //At this point, if we don't have a frame object, might as well abort.
              Alert("Could not find a starting point for the search. Cannot continue." , Constants.FF_ALERT_CONTINUE_WARN);
              return replacementCounter;
          //store the original text selection as an amenity to restore after the action.
          restoreTR = docum.TextSelection;
          //now, set up the starting text range as the very beginning
          //of the flow. We'll move straight from beginning to end.
          tr.beg.obj = tr.end.obj = frame.FirstPgf;
          tr.beg.offset = tr.end.offset = 0;
          //set up our find parameters. We want to configure it to look
          //for a string and perhaps be case sensitive. We don't need
          //the find to wrap because we are controlling the flow from
          //beginning to end.
          findParams = AllocatePropVals(2);
          findParams[0].propIdent.num = Constants.FS_FindText;
          findParams[0].propVal.valType = Constants.FT_String;
          findParams[0].propVal.sval = findString;
          findParams[1].propIdent.num = Constants.FS_FindCustomizationFlags;
          findParams[1].propVal.valType = Constants.FT_Integer;
          findParams[1].propVal.ival = 0;
          //initialize the errno global, which will be used to
          //track the progress of the find and replace
          FA_errno = Constants.FE_Success;
          //and do an initial find to get started.
          tr = docum.Find(tr.beg, findParams);
          //now, run the find and replace loop as long as we keep finding things.
          //The loop counter is just an emergency back door in case something
          //goes critically wrong and causes an endless loop.
          while(FA_errno === Constants.FE_Success && loopCounter++ < 1000){
              //set up the text range to clear the original text
              docum.TextSelection = tr;
              //clear it
              // insert the variable at the found 
              addVariable(docum, tr, 0, Constants.FO_Var, replaceVarName);
              /* Old code that replaced found text with text
              //insert the new text. We should be able to use the
              //original beginning of the text range where the old text was
              docum.AddText(tr.beg, replaceVarName);
              ////// This is where I should create the variable.
              //now, lets jimmy the text range in memory to place it directly
              //after the string we just inserted, so the find picks back up after that.
              tr.beg.offset += replaceVarName.length;
              //increment our return counter
              if(FA_errno === Constants.FE_Success){
              //...and find the next instance. We'll reset FA_errno again just in case
              //something screwy happened while we were replacing text.
              FA_errno = Constants.FE_Success;
              tr = docum.Find(tr.beg, findParams);
          //we're done. Restore the document to it's original area of display
          docum.TextSelection = restoreTR;
          return replacementCounter;


      Add Variable function:

      function addVariable(doc, tr, offset, type, format) {
          // Use Create Variable function like this: createVariable(doc, pgf, 0, Constants.FO_Var, "Current Date (Long)", "Index");
          var tl, textVar;
          tl = new TextLoc(tr, offset);
          //Get the variable object by name
          textVar = GetNamedVarFmt(format);
          //Insert into variable
          doc.addText(variable, tl);
          //Return something because... #whynot?
          return 1;
          //textVar = doc.NewAnchoredFormattedObject(type, format, tl);
          //textVar = doc.NewAnchoredFormattedVar(format, tl);


      The characters to be replaced are in an array like this:

      var find_me = [["char-to-find","var-name-for-replace"],["char-to-find","var-name-for-replace"]];


      The function is called via a loop that sends "char-to-find" and "var-name-for-replace" to FindAndReplaceEdufont like this:

      // Setup Document Variables
      var currentDocument = app.ActiveDoc;
      var mainTextFlow = app.ActiveDoc.MainFlowInDoc;
      // Find all old edufont characters and replace them with the variables
      for (i = 0; i < find_me.length; i+=1) {
          FindAndReplaceEdufont(currentDocument,  mainTextFlow, find_me[i][0], find_me[i][1]);


      Like I mentioned, it currently replaces the character with the text inside the variable, not with an instance of the variable itself. I've tried using NewAnchoredFormattedObject() and NewAnchoredFormattedVar() and they both had the same results (commented out in the above code).


      So... what am I doing wrong? I can't seem to crack this nut at all...


      Any help would be very much appreciated!