• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

Need help applying markers through document

Community Beginner ,
Feb 25, 2019 Feb 25, 2019

Copy link to clipboard

Copied

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;

}

TOPICS
Scripting

Views

948

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

Community Expert , Feb 25, 2019 Feb 25, 2019

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 =

...

Votes

Translate

Translate
Advocate ,
Feb 25, 2019 Feb 25, 2019

Copy link to clipboard

Copied

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.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Feb 25, 2019 Feb 25, 2019

Copy link to clipboard

Copied

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.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Feb 25, 2019 Feb 25, 2019

Copy link to clipboard

Copied

#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.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Feb 25, 2019 Feb 25, 2019

Copy link to clipboard

Copied

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.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Feb 25, 2019 Feb 25, 2019

Copy link to clipboard

Copied

Since we are applying a regular expression to the Link text, we will need to get the text itself from each Link TextRange. Here is a little utility function that we can use; in fact, you can use this in a lot of your scripts:

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

}

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Feb 25, 2019 Feb 25, 2019

Copy link to clipboard

Copied

Here is the entire script so far:

#target framemaker

var doc = app.ActiveDoc;

var pgf = doc.TextSelection.beg.obj;

processLinks (pgf, doc);

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 and display the text.

                doc.TextSelection = textRange;

                alert (getText (textRange, doc));

            }

            end = begin;

        }

    }

    if (end > 0) {

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

        charTag = getCharacterFormatName (textRange, doc);

        if (charTag === "Link") {

            // Select and display the text.

            doc.TextSelection = textRange;

            alert (getText (textRange, doc));

        }

    }

}

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;

}

Notice that I am calling the new getText function on lines 20 and 31 to display the text that has the Link character format applied. You should be able to click in one of your paragraphs and run the code and get the correct results so far.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Feb 25, 2019 Feb 25, 2019

Copy link to clipboard

Copied

Now I need to introduce my regular expression because I need to find only certain strings that have Link applied to them. I could define my regular expression inside the processLinks function but there are two reasons why I won't:

1) I want to keep processLinks as simple as possible and it is already fairly long with the tests we need.

2) There is some overhead to creating a regular expression so I want to create it once instead of every time processLinks is called.

Here is what I have so far:

#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 = 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") {

                if (regex.test (getText (textRange, doc)) === true) {

                    // Select and display the text.

                    doc.TextSelection = textRange;

                    alert (getText (textRange, doc));

                }

            }

            end = begin;

        }

    }

    if (end > 0) {

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

        charTag = getCharacterFormatName (textRange, doc);

        if (charTag === "Link") {

            if (regex.test (getText (textRange, doc)) === true) {

                // Select and display the text.

                doc.TextSelection = textRange;

                alert (getText (textRange, doc));

            }

        }

    }

}

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;

}

Here are the changes from the previous script:

1) I like to declare all of my variables at the top of the script (or function) so I am using a single var statement on line 3. This also means I need to remove the var keyword from the beginning of lines 5, 6, and 8.

2) Line 6 is where I create my regular expression object.

3) Line 10 is where I pass the regular expression into the processLinks function; line 12 is modified to receive the regular expression into the function.

4) Lines 23 and 36 are where the regular expression is applied to the text to see if there is a match. With the nested if statements we are testing two things: Is the Link character format applied and, if so, does the text match the regular expression?

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Feb 25, 2019 Feb 25, 2019

Copy link to clipboard

Copied

OK, now I am really going to annoy you because I am going to put the previous code aside and start a brand new script. We need to see if a Hypertext marker is already applied to the text and so I want to break this out into a separate task. Why? Because we got the previous code working and this adds a wrinkle to it that should be tackled by itself. I will explain in a minute, but first, highlight some text that has a marker in it and run this code:

#target framemaker

var doc, textRange, marker;

doc = app.ActiveDoc;

textRange = doc.TextSelection;

marker = getMarkerFromRange (textRange, "Hypertext", doc);

alert (marker);

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;

        }

    }

}

Lines 1 through 6 set everything up for our test and line 8 calls our new function, which starts on line 11. Here is why it is worth breaking off and doing this separately: we need to make some decisions on how thorough we have to be with this test. Currently, we just return a marker object if it matches the name parameter and this might be good enough. But, here are some questions to consider:

1) What if there is more than one marker that matches the name parameter?

2) Is it enough to test for the marker type, or do we need to specifically test for certain text in the marker?

Anyway, it is easier to fine-tune and test this function separately before introducing it into the main script.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Feb 25, 2019 Feb 25, 2019

Copy link to clipboard

Copied

Back to our main script with the new code introduced. I am assuming that it is enough just to test for the presence of any Hypertext marker; if one exists, then we skip that text range. If you need a more involved test, then modify the getMarkerFromRange function accordingly.

#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 = 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") {

                if (regex.test (getText (textRange, doc)) === true) {

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

                        // Select and display the text.

                        doc.TextSelection = textRange;

                        alert (getText (textRange, doc));

                    }

                }

            }

            end = begin;

        }

    }

    if (end > 0) {

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

        charTag = getCharacterFormatName (textRange, doc);

        if (charTag === "Link") {

            if (regex.test (getText (textRange, doc)) === true) {

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

                    // Select and display the text.

                    doc.TextSelection = textRange;

                    alert (getText (textRange, doc));

                }

            }

        }

    }

}

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;

}

You will see that lines 24 and 39 add our marker test.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Feb 25, 2019 Feb 25, 2019

Copy link to clipboard

Copied

Another short detour here: how to insert a marker at a particular text location. Select a range of text in a test document and run this code:

#target framemaker

var doc, textRange, marker;

doc = app.ActiveDoc;

textRange = doc.TextSelection;

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

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; 

    } 

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; 

The addMarker function takes four arguments: the TextLoc where the marker should be inserted, the marker type name, the marker text, and the Doc object. However, this function has a "dependency" function (getMarkerType) that gets the MarkerType object of the marker that you want to insert. The benefit of the getMarkerType function is that it allows you to use a markerName that does not exist in the current document. Here, we are specifying Hypertext, which exists in every FrameMaker document, but if you wanted to use the addMarker function to add a custom marker type, the getMarkerType function would create the marker type if it didn't currently exist in the document.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Feb 25, 2019 Feb 25, 2019

Copy link to clipboard

Copied

I noticed in your specs that you are using the Link text in your marker text so let's modify the processLinks function to capture the text.

function processLinks (pgf, doc, regex) {

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

   

    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) {

                        // Select and display the text.

                        doc.TextSelection = textRange;

                        alert (text);

                    }

                }

            }

            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) {

                    // Select and display the text.

                    doc.TextSelection = textRange;

                    alert (text);

                }

            }

        }

    }

}

See lines 3, 12, 13, 17, 28, 29, and 33, where we are using the new text variable. The general rule I use this this: if I am calling a function multiple times (like getText), it is sometimes better to call it once and store the result in a variable.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Feb 25, 2019 Feb 25, 2019

Copy link to clipboard

Copied

OK, this thing finally does something useful to the paragraph in the document that contains the insertion point. See if you can follow it. I have commented out lines 26 and 40 and I will tell you why below the code.

#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 = Constants.FV_OBJ_END_OFFSET - 1, begin = 0, textRange, charTag, text;

   

    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) {

                        //addMarker (textRange.beg, "Hypertext", "gotolink " + text.toLowerCase (), 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) {

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

                }

            }

        }

    }

}

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; 

    } 

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;

}

If you uncomment lines 26 and 40, the code will do what you want. However, there is one big problem: the Hypertext marker will be at the beginning of the text and won't have the Link character format applied to it. You need to have the Link character format applied to it. This will require one short detour in the next post.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Feb 25, 2019 Feb 25, 2019

Copy link to clipboard

Copied

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.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Feb 25, 2019 Feb 25, 2019

Copy link to clipboard

Copied

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;

}

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Feb 25, 2019 Feb 25, 2019

Copy link to clipboard

Copied

At this point in the development process, I will click around in my sample document and run the code. I want to test it on individual paragraphs before I turn it loose on an entire document or book. But because of the way I have developed it from the bottom up, I am pretty confident that it is going to work.

Another benefit to this approach is that I have a bunch of reusable functions that I can use in other scripts with little or no modifications. I have successfully developed tens of thousands of scripts like this over the last 20 years.

OK, here is how you put it all together: replace everything above the processLinks function with this:

#target framemaker

var doc, pgf, regex;

doc = app.ActiveDoc;

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

pgf = doc.FirstPgfInDoc;

while (pgf.ObjectValid () === 1) {

    processLinks (pgf, doc, regex);

    pgf = pgf.NextPgfInDoc;

}

Now it should process all of the paragraphs in the document. But before testing it, expand it one more time; replace everything above the processLinks function with this:

#target framemaker

main ();

function main () {

   

    var doc;

   

    doc = app.ActiveDoc;

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

        processDoc (doc);

    }

    else {

        alert ("There is no active document.", "www.frameexpert.com");

    }

}

function processDoc (doc) {

   

    var pgf, regex;

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

    pgf = doc.FirstPgfInDoc;

    while (pgf.ObjectValid () === 1) {

        processLinks (pgf, doc, regex);

        pgf = pgf.NextPgfInDoc;

    }

}

Now you should have a complete script! The end.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Feb 25, 2019 Feb 25, 2019

Copy link to clipboard

Copied

frameexpert

WOW! That is amazing! Thank you so much. I have spent hours and hours trying to get this to work. I did start out with small tasks, like you recommend, but when I tried to put them together, it got all convoluted. I've been trying to understand Extendscript but it's hard. I'm going to study your code, change things, see what happens, see what I can make it do. I am determined to learn this! Maybe someday I'll be able to help somebody out, too.

Thanks again,

Julie

(Which one would you like for me to click as the Correct Answer?)

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Feb 25, 2019 Feb 25, 2019

Copy link to clipboard

Copied

LATEST

How about number 14? :-). Thank you.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines