Copy link to clipboard
Copied
I am desigining a script to find any instances where ctrl+b and ctrl+i are applied to body text and then replace those character format overrides with Italic or Bold chartag. Here's what the script is supposed to do:
This script does work on the first character format override found however it ignores any other overrides in the same paragraph. The cause of this is that the while loop updates the text loc that the find method uses to the next paragraph in flow. I suspect that i need to add an inner loop that goes through all the text in a single paragraph, where at teach iteration the text loc used by the find method is based on the same paragraph but the offset is modified. I am just not sure how to do that.
function removeOverrides (pDoc)
{
var vDocStart = pDoc.MainFlowInDoc.FirstTextFrameInFlow.FirstPgf;
var vBoldFmt=getCharFmt (pDoc, 'Bold')
var vItalicFmt=getCharFmt (pDoc, 'Italic')
initFA_errno ();
while (FA_errno==Constants.FE_Success)
{
var vTextLoc = new TextLoc(vDocStart,0);
var vFindParams=findOverrideParams (pDoc);
var vTextRange=pDoc.Find(vTextLoc,vFindParams);
if (vTextRange.beg.obj.ObjectValid())
{
var vTextItems=pDoc.GetTextForRange (vTextRange, Constants.FTI_CharPropsChange)
if (vTextItems.length==!0 )
{
if (vTextItems[0].idata==Constants.FTF_WEIGHT)
{
pDoc.SetTextProps (vTextRange, vBoldFmt.GetProps())
}
if (vTextItems[0].idata==Constants.FTF_ANGLE)
{
pDoc.SetTextProps (vTextRange, vItalicFmt.GetProps())
}
} else (Log (vLogFileName, '\nERROR: No items were found in the text format array but format override was found: '+pDoc.Name))
}
vDocStart=vDocStart.NextPgfInFlow;
}
}
function findOverrideParams (pDoc)
{
var vFindParams = AllocatePropVals(1);
vFindParams[0].propIdent.num = Constants.FS_FindObject;
vFindParams[0].propVal.valType = Constants.FT_Integer;
vFindParams[0].propVal.ival = Constants.FV_FindCharacterFormatOverride;
return vFindParams;
}
Another approach (which is what I've usually done) is after the first search to set vTextLoc = vTextRange.end
Either way, you might consider moving your initializing of vTextLoc and vFindParams outside of the while loop. The parameters don't change, why redefine them for each search?
Copy link to clipboard
Copied
have you tried moving backward through the document.
I don't know if this works for that case but in similar cases, where I delete things in a document, that worked for me
Markus
Copy link to clipboard
Copied
Hi Wiedenmaier,
Tha's a clever idea. I tried it out. Unfortunately it didn't do anything for me. Not sure why though since logically I think that should work. That said Oliver's suggestion about updating the textloc with the end of text range does work. I'd recommend that method if you ever need to use the find method in your scripts.
Copy link to clipboard
Copied
Another approach (which is what I've usually done) is after the first search to set vTextLoc = vTextRange.end
Either way, you might consider moving your initializing of vTextLoc and vFindParams outside of the while loop. The parameters don't change, why redefine them for each search?
Copy link to clipboard
Copied
I feel stupid. It never occurred to me that yes a text range includes two text locations, one of which is the end of the text range that I could use to dynamically update where the find method starts!! Oui, thank you for the pointer. The moment you mentioned it, I had a eureka moment.
As for the initializing the variables, you are absolutely correct about that. I moved those out of the while loop and that significantly sped up my script. Thanks for that too. As to why I did that, I think its bad scripting habit. I tend to place related things right next to one another, when they should be placed outside loops in order to optimize the script.
One caveat though: the script seems to be working 95% of the time. There's a small subset of the text in the doc, which has bold or italic overrides but the script does not fix.I haven't been able to isolate a pattern though it does tend to happen when the override line breaks within a paragraph. If I can reliably reproduce this inconsistency, I'll update this discussion.
In any case, getting rid of 95% of my overrides and then cleaning up the remaining 5% manually with the find dialog box is still quite worthwhile.
Thanks again. Your insight and advice on scripting is immensely appreciated.
Copy link to clipboard
Copied
In general, using the Find method to locate FrameMaker objects is not the best way to go. I have written thousands of FrameScript scripts since 1998, and have probably used Find less than a dozen times. To find tables, you can simply loop through the tables in the document. Or, you can use GetTextItems to get the tables from the main flow in document order. To locate character formatting, you can use GetTextItems on a paragraph to get the character property changes. This takes a bit more code than Find, but it is definitely more reliable and you don't have to manage text ranges and wrapping issues.
Rick
Copy link to clipboard
Copied
Hi Rick,
I actually deleted my table post because I realized I was doing something stupid. The find was returnig a text range where the beg and end objects were one and the same so of course it wasn't ever progressing beyond that point. That was happening because the table was the only thing in the text range.
That said I see your point now. I was seesawing back and forth between looping through the tables versus doing a find. I was opting for the find because as you pointed out less code. As it turns out though, the find brings a lot more trouble then going the table loop. So I'll bite the bullet and do the table looping with more code. Sounds like I'll get a lot less aggravation.
Thanks,
Joe
Copy link to clipboard
Copied
Hi Joe,
Here is some code that will find each table in the document's (doc) main text flow. For each table, it will call a processTable function (which you would define). In my post, I referred to GetTextItems, but the method is actually called GetText. Most text objects like paragraphs, flows, text frames, etc., have a GetText method.
var flow = doc.MainFlowInDoc;
var textItems = flow.GetText(Constants.FTI_TblAnchor);
for (var i = 0; i < textItems.len; i += 1) {
tbl = doc.GetUniqueObject(Constants.FO_Tbl, textItems.obj.Unique);
processTable(tbl, doc);
}
Rick
Copy link to clipboard
Copied
Hi Rick,
I tried using your code but I am not getting the result I'd like. I want to cut the table, insert a new paragraph, and then paste the table into the new paragraph I created. I had that successfully working with the find method but it only worked on the first table found.
Using the suggested for loop, I originally couldn't get the cut to work however after doing some research I figured out the appropriate text locs and offsets that the text range needed to be in order to just cut the table. So this will actually cut the table:
var vDoc=app.ActiveDoc;
var vFlow = vDoc.MainFlowInDoc;
var vTextItems = vFlow.GetText(Constants.FTI_TblAnchor);
for (var i = 0; i < vTextItems.len; i += 1)
{
var vTbl = vDoc.GetUniqueObject(Constants.FO_Tbl, vTextItems.obj.Unique);
var vTblTextRange=new TextRange ();
vTblTextRange.beg.obj=vTbl.TextLoc.obj;
vTblTextRange.beg.offset = vTbl.TextLoc.offset;
vTblTextRange.end.obj=vTbl.TextLoc.obj;
vTblTextRange.end.offset = Constants.FV_OBJ_END_OFFSET;
vDoc.TextSelection=vTblTextRange;
vDoc.Cut (0);
}
However, I haven't been able to figure out how to insert a paragraph right after the paragraph that table was originally part of and then paste the table into that new paragraph.
Copy link to clipboard
Copied
Hi Rick,
Well, following up on the previous posting, I actually did figure out how to make it work but it seems rather inefficient. I can insert a new paragraph right after the paragraph that has the table anchor but when i cut the table, i also cut the paragraph i just created. So to get around that, I create two paragraphs, so that there will be one paragraph left after i do the cut and that is the paragraph that I paste the table into. Once the table is pasted into the new paragraph, I delete the other paragraph that I created.
Here's the updated code from the for loop:
var vTbl = vDoc.GetUniqueObject(Constants.FO_Tbl, vTextItems.obj.Unique);
var vAnchorPgf=vDoc.NewSeriesPgf (vTbl.TextLoc.obj)
var vAnchorPgf2=vDoc.NewSeriesPgf (vAnchorPgf);
var vAnchorTextLoc=new TextLoc (vAnchorPgf2, 0);
var vTblTextRange=new TextRange ();
vTblTextRange.beg.obj=vTbl.TextLoc.obj;
vTblTextRange.beg.offset = vTbl.TextLoc.offset;
vTblTextRange.end.obj=vTbl.TextLoc.obj;
vTblTextRange.end.offset = Constants.FV_OBJ_END_OFFSET
vDoc.TextSelection=vTblTextRange;
vDoc.Cut (0);
vTblTextRange.beg.obj=vAnchorTextLoc.obj;
vTblTextRange.beg.offset = 0;
vTblTextRange.end.obj=vAnchorTextLoc.obj;
vTblTextRange.end.offset =0;
vDoc.TextSelection=vTblTextRange;
vDoc.Paste (0);
vAnchorPgf2.Delete();
Copy link to clipboard
Copied
There is something I've noticed about Constants.FV_OBJ_END_OFFSET. Instead of a reasonable number, for example, 57, it seems to be a long number like 1342177280.
Is this expected? If not, could it be causing FM to crash after setting a text range?
Thanks,
Jason
Copy link to clipboard
Copied
Constants.FV_OBJ_END_OFFSET is the last position of a textrange (text location).
This is expected. I used it several times in ES and FDK progamming, with no issues
Markus