Skip to main content
juliem31820066
Participating Frequently
February 25, 2019
Answered

Need help applying markers through document

  • February 25, 2019
  • 2 replies
  • 1638 views

I am working on a script to search through a document and add gotolink markers to specific text.

For example, in this text,  "Lorem ipsum dolor sit amet, consectetur adipiscing F1, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim F2 minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip T1 ea commodo consequat," F1, F2 and T1 have the character tag "Link" and need the gotolink marker applied. The word "quis" is also a link, but should be skipped as it is not what we are looking for.

Thanks to script I've found and help I've received here, I've got a good start, but now I'm stuck. The script loops through and finds all the text with the character tag "link." However, it only read the text and applies the gotolink marker to the first instance. In the example above, If there are three instances of text with the "link" character tag, it will apply  "gotolink  f1" three times to the first instance.

This is what I want the script to do.

1. Loop through text and find all tagged "Link"

2. Go to the first instance. If there's already a gotolink marker, skip and go to the next. I'm not sure where to do this.

3. If there is not a gotolink marker, select the text range.

4. Read the text.

5. If the text is the right kind (regex), create a marker.

6. Go to the next one. I can't get it to read/apply marker to subsequent links.

Code is below. I am using Framemaker 19.

Thank you for your help!

Julie

-------------

var doc = app.ActiveDoc;

var fmtName = "Link";

var pgf = doc.FirstPgfInDoc;

var markerList = [];

var tloc = new TextLoc (pgf, 0);

var locTextRange = new TextRange (tloc, tloc);

doc.TextSelection = locTextRange;

if (!doc.ObjectValid ()) { 

      alert ("There is no active document."); 

        }

    else  {  // gather all markers-locations(objects) and store them in an array (MarkerList)

     

    var findParams = getFindParams ();

    var foundTextRange = doc.Find(tloc, findParams);

    while (foundTextRange.beg.obj.ObjectValid())

            {

                markerList.push(foundTextRange);

                tloc = foundTextRange.end;

                foundTextRange = doc.Find(tloc, findParams);

            } 

    var foundMarker = [];

    for (var i = 0; i < markerList.length; i++)

        {

        var markerTI = doc.GetTextForRange (markerList, Constants.FP_CharTag);

        doc.TextSelection = markerList;

        alert("found link") //All the links are being found.

     

        getFigOrTableNum (pgf); //Somewhere in here I need this function to apply to each instance of the text selection found. Right now, it's only reading and applying to the first found "link"

        doc.ScrollToText (markerList);

        for (var x = 0; x < markerTI.length; x++)

          {

            var textItem = markerTI;

            foundMarker.push(textItem.obj);//store the marker objects       

            }

        }

    } 

function getFindParams () {  //finding text that has the character tag "Link"

   var findParams = new PropVals();

   var propVal = new PropVal();

            propVal.propIdent.num = Constants.FS_FindCharTag; 

            propVal.propVal.valType = Constants.FT_String; 

            propVal.propVal.sval = fmtName;

            findParams.push(propVal);

    propVal = new PropVal();

            propVal.propIdent.num = Constants.FS_FindWrap; 

            propVal.propVal.valType = Constants.FT_Integer; 

            propVal.propVal.ival = false;

            findParams.push(propVal);

    return findParams; 

}         

function getTextRange () {

    var textRange = new TextRange();

      textRange.beg.obj = textRange.end.obj = doc.MainFlowInDoc.FirstTextFrameInFlow.FirstPgf;

      textRange.beg.offset = textRange.end.offset = 0;

      doc.TextSelection = textRange;

      textRange = doc.Find(textRange.beg, findParams);

    return textRange;

  }

function getMarker (pgf, markerType, text) { //See if a marker exists in a paragraph. I don't have this function called yet... couldn't get it to work, but I want to use this to see if a "link" already has a marker or not. If not, create the marker. If it does, go on to the next link.

    var markers = [], marker, textList, i;

    textList = pgf.GetText (Constants.FTI_MarkerAnchor);

    for (i = 0; i < textList.len; i += 1) {

        marker = textList.obj;

        if (marker.MarkerTypeId.Name === markerType) {

            if (marker.MarkerText.indexOf (text) > -1) {

                return marker;

            }

        }

    }

}

function getFigOrTableNum (pgf) {

    var doc = app.ActiveDoc; 

    var findParams = getFindParams ();

    var text = "", textItems, i;

    var textRange = getTextRange ();

    // Get a list of the strings in the text object or text range.

    if (textRange.constructor.name !== "TextRange") {

        textItems = textRange.GetText(Constants.FTI_String);

        alert(textItems);

    } else {

        textItems = doc.GetTextForRange(textRange, Constants.FTI_String);

    }

    // Concatenate the strings.

    for (i = 0; i < textItems.length; i += 1) {

        text += (textItems.sdata);

    }

        alert ("This link text is "+ text); // it is getting and reading the text, but only from the first one  

    if (text.match('(AF|AT|[FT])([0-9]+)') != null ) {

        createMarker (text);

        alert("marker created");

        }

    else {

        alert("this link is not a figure or table");

        }

}   

function createMarker (text) { // creates the marker

    var doc = app.ActiveDoc;

    var textRange = getTextRange ();

    var marker = doc.NewAnchoredObject(Constants.FO_Marker, (textRange.beg, textRange.end));

    var markerType = doc.GetNamedObject(Constants.FO_MarkerType, "Hypertext");

    marker.MarkerTypeId = markerType;

    marker.MarkerText = "gotolink " + text.toLowerCase ();

    return 1;

}

This topic has been closed for replies.
Correct answer frameexpert

Here is the detour code

#target framemaker

var doc, textRange, marker;

doc = app.ActiveDoc;

textRange = doc.TextSelection;

marker = addMarker (textRange.beg, "Hypertext", "gotolink xyz", doc);

applyCharFmtToMarker (marker, "Link", doc);

function applyCharFmtToMarker (marker, name, doc) {

   

    var textRange;

   

    textRange = new TextRange (

        new TextLoc (marker.TextLoc.obj, marker.TextLoc.offset),

        new TextLoc (marker.TextLoc.obj, marker.TextLoc.offset + 1));

    applyCharFmt (textRange, name, doc);   

}

function applyCharFmt (textRange, name, doc) {

   

    var charFmt = 0;

   

    charFmt = doc.GetNamedCharFmt (name);

    if (charFmt.ObjectValid()) {

        doc.SetTextProps (textRange, charFmt.GetProps());

    }

}

function addMarker (textLoc, markerName, markerText, doc) { 

    

    var marker, markerType; 

 

    // Get the specified marker type. 

    markerType = getMarkerType (markerName, doc); 

    if (markerType.ObjectValid () === 1) { 

        // Insert the marker at the text location and set its properties. 

        marker = doc.NewAnchoredMarker (textLoc); 

        marker.MarkerTypeId = markerType; 

        marker.MarkerText = markerText; 

    }

    return marker;

function getMarkerType (markerName, doc) { 

    

    // Get a marker type from the document; if it doesn't exist, create it.

   

    var markerType = doc.GetNamedMarkerType (markerName); 

    if (markerType.ObjectValid () == 0) { 

        markerType = doc.NewNamedMarkerType (markerName); 

    } 

    return markerType; 

Two new functions here: applyCharFmtToMarker will apply a character format to any new marker that we add. It has a dependency function called applyCharFmt starting on line 20.

In testing this, I discovered an error in my code. When we insert the marker with the addMarker function, we need to return the Marker object so we can apply the character format to it. The previous iteration of the function did not have the return statement on line 43 above.


We should be finally ready to put it all together. Here is the completed code for a single paragraph containing the insertion point:

#target framemaker

var doc, pgf, regex;

doc = app.ActiveDoc;

regex = /^[FT]\d+$/;

pgf = doc.TextSelection.beg.obj;

processLinks (pgf, doc, regex);

function processLinks (pgf, doc, regex) {

    var end, begin = 0, textRange, charTag, text, marker;

   

    end = Constants.FV_OBJ_END_OFFSET - 1

   

    var textList = pgf.GetText (Constants.FTI_CharPropsChange);

    for (var i = textList.length - 1; i >= 0; i -= 1) {

        begin = textList.offset;

        if (begin !== end) {

            textRange = new TextRange (new TextLoc (pgf, begin), new TextLoc (pgf, end));

            charTag = getCharacterFormatName (textRange, doc);

            if (charTag === "Link") {

                text = getText (textRange, doc);

                if (regex.test (text) === true) {

                    if (getMarkerFromRange (textRange, "Hypertext", doc) === undefined) {

                        marker = addMarker (textRange.beg, "Hypertext", "gotolink " + text.toLowerCase (), doc);

                        applyCharFmtToMarker (marker, "Link", doc);

                    }

                }

            }

            end = begin;

        }

    }

    if (end > 0) {

        textRange = new TextRange (new TextLoc (pgf,0), new TextLoc (pgf,end));

        charTag = getCharacterFormatName (textRange, doc);

        if (charTag === "Link") {

            text = getText (textRange, doc);

            if (regex.test (text) === true) {

                if (getMarkerFromRange (textRange, "Hypertext", doc) === undefined) {

                    marker = addMarker (textRange.beg, "Hypertext", "gotolink " + text.toLowerCase (), doc);

                    applyCharFmtToMarker (marker, "Link", doc);

                }

            }

        }

    }

}

function applyCharFmtToMarker (marker, name, doc) {

   

    var textRange;

   

    textRange = new TextRange (

        new TextLoc (marker.TextLoc.obj, marker.TextLoc.offset),

        new TextLoc (marker.TextLoc.obj, marker.TextLoc.offset + 1));

    applyCharFmt (textRange, name, doc);   

}

function applyCharFmt (textRange, name, doc) {

   

    var charFmt = 0;

   

    charFmt = doc.GetNamedCharFmt (name);

    if (charFmt.ObjectValid()) {

        doc.SetTextProps (textRange, charFmt.GetProps());

    }

}

function addMarker (textLoc, markerName, markerText, doc) { 

    

    var marker, markerType; 

 

    // Get the specified marker type. 

    markerType = getMarkerType (markerName, doc); 

    if (markerType.ObjectValid () === 1) { 

        // Insert the marker at the text location and set its properties. 

        marker = doc.NewAnchoredMarker (textLoc); 

        marker.MarkerTypeId = markerType; 

        marker.MarkerText = markerText; 

    }

    return marker;

function getMarkerType (markerName, doc) { 

    

    // Get a marker type from the document; if it doesn't exist, create it.

   

    var markerType = doc.GetNamedMarkerType (markerName); 

    if (markerType.ObjectValid () == 0) { 

        markerType = doc.NewNamedMarkerType (markerName); 

    } 

    return markerType; 

function getMarkerFromRange (textRange, name, doc) {

   

    var textItems, i, marker;

   

    textItems = doc.GetTextForRange (textRange, Constants.FTI_MarkerAnchor);

    for (i = 0; i < textItems.length; i += 1) {

        marker = textItems.obj;

        if (marker.MarkerTypeId.Name === name) {

            return marker;

        }

    }

}

function getText (textObj, doc) {

   

    // Gets the text from the text object or text range.

    var text = "", textItems, i;

   

    // Get a list of the strings in the text object or text range.

    if (textObj.constructor.name !== "TextRange") {

        textItems = textObj.GetText(Constants.FTI_String);

    } else {

         textItems = doc.GetTextForRange(textObj, Constants.FTI_String);

    }

    // Concatenate the strings.

    for (i = 0; i < textItems.len; i += 1) {

        text += (textItems.sdata);

    }

    return text; // Return the text

}

function getCharacterFormatName (textRange, doc) {

   

    var prop;

   

    // Get the character format applied to the text range.

    prop = doc.GetTextPropVal (textRange.beg, Constants.FP_CharTag);

    return prop.propVal.sval;

}

2 replies

frameexpert
Community Expert
Community Expert
February 25, 2019

Hi Julie,

OK, I am going to help you here, but you will have to be able to take some constructive criticism. First of all, your approach is too complicated; you need to divide this up into discreet tasks so that you can see what you are doing and get each piece working before combining everything into a single script. Second, it helps others help you if you have things split out into tasks. I am hesitant to take a big script like yours and help because it takes too much effort to stage things on my machine, especially without a sample document.

Here is the first task: Take the paragraph where your cursor is and loop through all of the distinct character formats applied:

#target framemaker

var doc = app.ActiveDoc;

var pgf = doc.TextSelection.beg.obj;

processPropertyChanges (pgf, doc);

function processPropertyChanges (pgf, doc) {

    var end = Constants.FV_OBJ_END_OFFSET - 1, begin = 0, textRange, charTag;

   

    var textList = pgf.GetText (Constants.FTI_CharPropsChange);

    for (var i = textList.length - 1; i >= 0; i -= 1) {

        begin = textList.offset;

        if (begin !== end) {

            textRange = new TextRange (new TextLoc (pgf, begin), new TextLoc (pgf, end));

            charTag = getCharacterFormatName (textRange, doc);

            // Select the text and display the character format applied.

            doc.TextSelection = textRange;

            alert (charTag);

            end = begin;

        }

    }

    if (end > 0) {

        textRange = new TextRange (new TextLoc (pgf,0), new TextLoc (pgf,end));

        charTag = getCharacterFormatName (textRange, doc);

        // Select the text and display the character format applied.

        doc.TextSelection = textRange;

        alert (charTag);

    }

}

function getCharacterFormatName (textRange, doc) {

   

    var prop;

   

    // Get the character format applied to the text range.

    prop = doc.GetTextPropVal (textRange.beg, Constants.FP_CharTag);

    return prop.propVal.sval;

}

The processPropertyChanges function will be the main function that is used for each paragraph in the document, but for now we just want to apply it to the paragraph containing the text cursor. Click in a paragraph that has character formats applied and run it. It's not very useful yet, but this is where we start.

A couple of notes: 1) Lines 18, 19, 27, and 28 will be unnecessary in the finished script, but they are here now so you can see the script "working".

2) We work through the paragraph backwards because later we may make changes to some of the text ranges by adding markers, etc. If we work from back to front, we won't mess up the offsets that were returned from line 11 as we work through the paragraph.

www.frameexpert.com
frameexpert
Community Expert
Community Expert
February 25, 2019

#target framemaker

var doc = app.ActiveDoc;

var pgf = doc.TextSelection.beg.obj;

processPropertyChanges (pgf, doc);

function processPropertyChanges (pgf, doc) {

    var end = Constants.FV_OBJ_END_OFFSET - 1, begin = 0, textRange, charTag;

  

    var textList = pgf.GetText (Constants.FTI_CharPropsChange);

    for (var i = textList.length - 1; i >= 0; i -= 1) {

        begin = textList.offset;

        if (begin !== end) {

            textRange = new TextRange (new TextLoc (pgf, begin), new TextLoc (pgf, end));

            charTag = getCharacterFormatName (textRange, doc);

            if (charTag !== "") {

                // Select the text and display the character format applied.

                doc.TextSelection = textRange;

                alert (charTag);

            }

            end = begin;

        }

    }

    if (end > 0) {

        textRange = new TextRange (new TextLoc (pgf,0), new TextLoc (pgf,end));

        charTag = getCharacterFormatName (textRange, doc);

        if (charTag !== "") {

            // Select the text and display the character format applied.

            doc.TextSelection = textRange;

            alert (charTag);

        }

    }

}

function getCharacterFormatName (textRange, doc) {

  

    var prop;

  

    // Get the character format applied to the text range.

    prop = doc.GetTextPropVal (textRange.beg, Constants.FP_CharTag);

    return prop.propVal.sval;

}

This is exactly the same as the previous post except we are excluding text ranges that don't have any character formatting applied. This is done with the if statements on lines 17 and 28.

One thing to note: You may wonder why we are using a function to get the character property name on lines 36 through 43 when it only takes two lines to get the name (41 and 42) There are three reasons:

1) The processPropertyChanges function is going to get a bit more complicated and I don't want to clutter it up more than necessary.

2) The processPropertyChanges function is one that will be good to reuse in other scripts, so it is best to keep it simple.

3) Having a distinct property for getting the character format name highlights how you get individual text properties and will be useful to see how it's done.

www.frameexpert.com
frameexpert
Community Expert
Community Expert
February 25, 2019

function processLinks (pgf, doc) {

    var end = Constants.FV_OBJ_END_OFFSET - 1, begin = 0, textRange, charTag;

  

    var textList = pgf.GetText (Constants.FTI_CharPropsChange);

    for (var i = textList.length - 1; i >= 0; i -= 1) {

        begin = textList.offset;

        if (begin !== end) {

            textRange = new TextRange (new TextLoc (pgf, begin), new TextLoc (pgf, end));

            charTag = getCharacterFormatName (textRange, doc);

            if (charTag === "Link") {

                // Select the text and display the character format applied.

                doc.TextSelection = textRange;

                alert (charTag);

            }

            end = begin;

        }

    }

    if (end > 0) {

        textRange = new TextRange (new TextLoc (pgf,0), new TextLoc (pgf,end));

        charTag = getCharacterFormatName (textRange, doc);

        if (charTag === "Link") {

            // Select the text and display the character format applied.

            doc.TextSelection = textRange;

            alert (charTag);

        }

    }

}

This time I am only posting the processPropertyChanges function with some changes to make it specific to your task.

1) I renamed it to processLinks so it reflects what you are doing in your particular script.

2) Lines 11 and 22 have been made to test specifically for the "Link" character format. We don't care about any other character formats that may be applied to the text in the paragraph.

www.frameexpert.com
4everJang
Legend
February 25, 2019

Hello Julie,

Try reversing the order in which you are adding the gotolink markers. When you add a marker, you change the offset of the text following the marker. Starting at the end of paragraphs makes that problem go away. Not sure if this is causing the problem but it is an easy way to find out.

Once you have your array of text locations, use pop( ) on it to pick the last one in the array and insert the marker (when it is not already there). To check if a marker was already added, you would have to write some code that finds the markers in the current paragraph and compare offsets. That is another issue to solve.