Copy link to clipboard
Copied
Hello fellows,
I am writing an extendscript that is supposed to loop through all pgfs and find all characters/strings with a certain condition applied (let's say, Comment).
The problem is that in the scripting guide, I could not find a function that retrieves the condition property of a character. I tried using the JS "charAt(i)" function, but it does not seem to work in ES. Any idea how to implement such a function?
Below is the 1st draft of the script.
var doc = app.ActiveDoc;
if doc.ObjectValid() {
checkCondFMt(doc);
}
else{
Alert("No section open");
}
function checkCondFMt(doc) {
var pgf = doc.MainFlowInDoc.FirstTextFrameInFlow.FirstPgf;
var CondObj=doc.GetNamedObject("Comment");
while (pgf.ObjectValid()){
var textItems =pgf.GetText(Constants.FTI_String | Constants.FTI_LineEnd);
var tr = new TextRange();
tr.beg.obj = tr.end.obj = textItems;
tr.beg.offset = 0;
for (var i = 0; i < tr.len; i++ ) {
var c = tr.charAt(i);
var PgfProps =doc.GetTextVal(c, Constants.FO_CondFmt);
alert (PgfProps.Name);
}
pgf=pgf.NextPgfInFlow;
}
}
Thank you for your input in advance!
OK, I see the problem now. JavaScript does not have "block-scope" for its variables. It has function scope, so your "i" variable is being used in more than one loop. You should use a different variable for the inner loop:
for (var j = 0; j < oCond.length; j += 1) {
if (oCond
.Name == CondObj.Name) { applyCharFmt(textRange, doc);
}
}
In the code sample above, I am using "j" for a counter for the inner loop.
In general, you can avoid problems like this by declari
...Copy link to clipboard
Copied
Roman, I have a 4-part post on my blog about removing conditions from text and table rows. Here is a link to the first one:
Removing Conditions From Text and Table Rows | FrameAutomation.com
-Rick
Copy link to clipboard
Copied
Hi Rick,
Thank you for your response and for a very useful and informative post!
In my specific case, I need to loop through all Pgfs and find all strings/characters to which the Comment condition is applied. My next step will be applying a char tag to those conditionalized strings/chars.
What I am trying to figure out is how to isolate the condtionalized strings and single characters in a Pgf. I found a JS function called "substr" or "substring" for isolating a range of chars and looping through them. Is this the one you would use?
Thanks again,
Roman
Copy link to clipboard
Copied
This is the part of the series I never got to :-). The following function will loop through all of the text in the selected paragraph and find each text range that has one or more conditions applied. You should be able to combine this with some of the code in my blog to test for a specific condition. Once you have the appropriate textRange, you should be able to apply a character format. Please let me know if you have any questions or comments.
#target framemaker
var doc = app.ActiveDoc;
var pgf = doc.TextSelection.beg.obj;processConditions (pgf, doc);
function processConditions (pgf, doc) {
var end = Constants.FV_OBJ_END_OFFSET, begin = 0, textRange = 0, prop;
var textList = pgf.GetText(Constants.FTI_CharPropsChange);
for (var i = textList.length - 1; i >= 0; i -= 1) {
if (Constants.FTF_CONDITIONTAG & textList.idata) {
begin = textList.offset;
if (begin !== end) {
textRange = new TextRange (new TextLoc (pgf, begin), new TextLoc (pgf, end));
prop = doc.GetTextPropVal (textRange.beg, Constants.FP_InCond);
if ((prop.propIdent.num) && (prop.propVal.osval.length !== 0)) {
alert (getText (textRange, doc));
}
}
end = begin;
}
}
if ((end < Constants.FV_OBJ_END_OFFSET) && (end > 0)) {
textRange = new TextRange (new TextLoc (pgf, 0), new TextLoc (pgf, end));
prop = doc.GetTextPropVal (textRange.beg, Constants.FP_InCond);
if ((prop.propIdent.num) && (prop.propVal.osval.length !== 0)) {
alert (getText (textRange, doc));
}
}
}function getText (textObj, doc) {
// Gets the text from the text object.var text = "";
// Get a list of the strings in the text object or text range.
if (textObj.constructor.name !== "TextRange") {
var textItems = textObj.GetText (Constants.FTI_String);
} else {
var textItems = doc.GetTextForRange (textObj, Constants.FTI_String);
}
// Concatenate the strings.
for (var i = 0; i < textItems.len; i += 1) {
text += (textItems.sdata);
}
return text; // Return the text
}
Copy link to clipboard
Copied
Hi Rick,
Thank you for your prompt response and for your help!
I wasn't even aware of the FTI_CharPropsChange constant existence. 🙂 This is an elegant solution for finding distinctive Pgf pieces.
I have a couple of questions regarding the code:
Line 6, 8 -- Novice's question: Why do you need to specify the pgf, doc variables in the processConditions() function? These are not the only vars used in it.
Line 13 -- Why do you decrement 1 here instead of incrementing by 1?
Lion 14 -- What does this line exactly say?
Line 23 -- Why is end equal to begin?
Line 26 -- What does this line exactly say?
General: it's not clear to me how you detect a single char with this code. AFAIK, FTI_String cannot include a single char, right?
Thank you for your explanations in advance!
Copy link to clipboard
Copied
Hi Roman,
Lines 6, 8: These are the parameters required by the function that have to be "passed in" from the outside.
Line 13: When you get the list of text items on line 12, it is a fixed list at the time you query the paragraph. If the script ends up changing the list somehow, you want to make sure that the loop doesn't get messed up by the changes. By working from the end of the paragraph to the beginning, you make sure that any changes you make to the paragraph doesn't affect the list of text items.
Line 14: When you get a list of FTI_CharPropsChange items, there is an internal list of exactly which properties have changed at that point. Since we are only interested in Condition Format changes, this line tests for that. Any other property change would be ignored.
Line 23: The variable end starts at the end of the paragraph. When a property change is detected, begin will be less than end. The script will then make a TextRange variable between begin and end (line 17), which will be a range of text containing a unique set of conditions (or no conditions at all). After all the tests are made, the script will start over at begin so you have end = begin.
Line 26: Actually, in this particular script, lines 26-33 aren't necessary.
I am not sure why you want to test a single character. This script will test one or more consecutive characters with exactly the same conditions applied. That means if there is a single character by itself, the script will detect it, or if there are multiple consecutive characters, the script will detect them. You could write a script to test a single character at a time, but I am not sure why you would want to. Maybe I am not understanding what you want to do.
Copy link to clipboard
Copied
Hi Rick,
Thank you for the explanations! The script logic is not trivial, I should say.
I'd like to test a single character just because in some cases, the condition is applied to a letter/number only. According to the FDK guide, FTI_String can only represent a string of characters and not a single character, so it's not clear to me how the script detects a single char.
Thanks again for your explanations!
My best wishes,
Roman
Copy link to clipboard
Copied
The script will find consecutively applied character formats, whether it is just a single character or a whole paragraph. Test it on some content and you will see. You'll have to trust me on this one :-).
Copy link to clipboard
Copied
Hi Rick,
Did you mean "consecutively applied condition formats"?
Trust has been established. I'm just trying to understand the internals of ES.
Thanks again,
Roman
Copy link to clipboard
Copied
Yes, sorry I meant Condition Formats.
Copy link to clipboard
Copied
Thanks a lot, Rick!
Copy link to clipboard
Copied
Hello fellows,
I've made some progress with the script. I've added a function that creates a Char format and a function that applies this format to the text that was conditionalized with a specific condition. I've also modified the script that Rick came up with so that it will search for strings with a specific condition applied. However, for some reason, the script gets stuck at some point, after applying the Char format to several strings it found in a document. I don't understand why the script enters some kind of a loop. Below is the code. Please, advise! Thanks!
#target framemaker
var doc = app.ActiveDoc;
var pgf = doc.FirstPgfInDoc;
Add_Char();
while (pgf.ObjectValid())
{
processConditions (pgf, doc);
pgf = pgf.NextPgfInDoc;
}
//Functions go here:
function Add_Char ()
{
var CharAdded = doc.NewNamedObject (Constants.FO_CharFmt, "Txt_Char");
var NSpellCheck = doc.GetNamedObject(Constants.FO_CharFmt, "NoSpellCheck");
var charprops = NSpellCheck.GetProps();
var bk_color = doc.GetNamedColor("Green");
var font_color = doc.GetNamedColor("Black");
var Bcolor_en = GetPropIndex(charprops, Constants.FP_UseBkColor);
var index = GetPropIndex(charprops, Constants.FP_BkColor);
CharAdded.UseBkColor = 1;
CharAdded.BkColor = bk_color;
CharAdded.SepOverride = font_color;
return(1);
}
function processConditions (pgf, doc) {
var end = Constants.FV_OBJ_END_OFFSET, begin = 0, textRange = 0, prop;
var CondObj=doc.GetNamedCondFmt("Comment");
//alert (CondObj.Name);
var textList = pgf.GetText(Constants.FTI_CharPropsChange);
for (var i = textList.length - 1; i >= 0; i -= 1) {
if (Constants.FTF_CONDITIONTAG & textList.idata) {
begin = textList.offset;
if (begin !== end) {
textRange = new TextRange (new TextLoc (pgf, begin), new TextLoc (pgf, end));
prop = doc.GetTextPropVal (textRange.beg, Constants.FP_InCond);
var oCond = prop.propVal.osval;
if ((prop.propIdent.num) && (oCond.length !== 0)) {
for (var i = 0; i < oCond.length; i += 1) {
if (oCond.Name == CondObj.Name) {
applyCharFmt(textRange, doc);
}
}
}
}
end = begin;
}
}
}
function applyCharFmt(textRange, doc)
{
var newCharFmt =doc.GetNamedCharFmt ("Txt_Char");
if(newCharFmt.ObjectValid() == true)
{
//Get the character format properties
var props = newCharFmt.GetProps();
doc.SetTextProps (textRange, props);
}
else alert("Could not find the character format!");
}
}
Copy link to clipboard
Copied
Hi Roman. Make sure you show all the conditions at the beginning of the script. This may be what is causing problems. -Rick
doc.ShowAll = 1;
Copy link to clipboard
Copied
Hi Rick,
I appreciate your response!
All conditions are exposed in the test document.
The status bar in ESTK shows "No errors" but there is a rolling indicator in the bottom right corner, as if the script execution has not ended.
In FM12, the char tag is applied to several strings with the specified condition. In FM10, nothing happens at all.
I am about to give up and port the script to Framescript, which seems to be a more stable scripting environment than Extendscript.
Best regards,
Roman
Copy link to clipboard
Copied
I haven't had a chance to actually test this, but one suggestion is to comment out some of the code and see if you can find out where it is getting stuck. For example, if you comment out the call to ApplyCharFmt does it still hang up?
Copy link to clipboard
Copied
Hi Rick,
Commenting out ApplyCharFmt and calling alert (GetText...) just for the sake of a test leads to the same behavior. Framemaker 10, for example, becomes completely irresponsive.
Code-wise, I don't understand what is wrong. The script execution result is "No Errors" but it does nothing except for making Framemaker faint.
Best regards,
Roman
Copy link to clipboard
Copied
Hi Rick,
I think I found what drives FM crazy when running the script.
FM gets stuck when the string(s) with the condition we are searching for have additional conditions applied.
If only the condition specified in the script is applied to the string (in this case, Comment), then the script works OK.
I guess, this issue has something to do with Constants.FTI_CharPropsChange, but I am not sure.
Thanks again,
Roman
Copy link to clipboard
Copied
OK, I see the problem now. JavaScript does not have "block-scope" for its variables. It has function scope, so your "i" variable is being used in more than one loop. You should use a different variable for the inner loop:
for (var j = 0; j < oCond.length; j += 1) {
if (oCond
.Name == CondObj.Name) { applyCharFmt(textRange, doc);
}
}
In the code sample above, I am using "j" for a counter for the inner loop.
In general, you can avoid problems like this by declaring all of your variables at the top of the function:
function functionName (param1, param2) {
var i, j, textlist; // etc.
// function code...
}
That makes it easier to "keep track" of the variables and make sure you are not using any more than once.
-Rick
Copy link to clipboard
Copied
Hi Rick,
Good catch! Thank you very much!
As usually, the devil is in the details.
I'd better start defining variables at the top of the function, as you kindly suggested.
My best regards,
Roman