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

Find/Replace?

New Here ,
Aug 25, 2011 Aug 25, 2011

Copy link to clipboard

Copied

I have looked all thru the documentation including the FDK and don't really see a way to do a global search and replace in extendscript.  The FDK documentation refers to F_APIFIND in the index and TOC for FIND/REPLACE but I don't see the paramters to set for it when I look there.  I assume there is a way to do it, and I'm just not smart enough to see it.  Any clues where I should look or samples somewhere?

TIA - Jim

TOPICS
Scripting

Views

7.4K

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

Mentor , Aug 26, 2011 Aug 26, 2011

Jim,

I see I didn't read your post closely enough. My apologies. Indeed, you'll need to control the iteration and string replacement yourself. It doesn't take too much code to do that, though. That's with the FDK... again, I don't know about ExtendScript.

Russ

Votes

Translate

Translate
Mentor ,
Aug 26, 2011 Aug 26, 2011

Copy link to clipboard

Copied

Hi Joe,

I can't speak for ExtendScript, but F_ApiFind() is well-defined on page 102 of the FDK Programmers Reference that I have (as installed with the FDK10). I have used it myself without issue. Are you sure you are looking in the right book?

Russ

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
New Here ,
Aug 26, 2011 Aug 26, 2011

Copy link to clipboard

Copied

I have looked and perhaps I just don't understand - but I don't see anything that lets me specify a replacement string.  Perhaps there is no such thing and I have to iterate thru the finds and replace the text myself?  Seems like there is a find/change dialog so there should be some equivalent functionality to that?

Thanks

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
Mentor ,
Aug 26, 2011 Aug 26, 2011

Copy link to clipboard

Copied

Jim,

I see I didn't read your post closely enough. My apologies. Indeed, you'll need to control the iteration and string replacement yourself. It doesn't take too much code to do that, though. That's with the FDK... again, I don't know about ExtendScript.

Russ

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
Mentor ,
Aug 27, 2011 Aug 27, 2011

Copy link to clipboard

Copied

Hi Jim,

I felt bad for accusing you of not being able to read. In an attempt to make up for that, I worked up a quick sample of how to do a search and replace with the FDK. I hope it may help some.

Russ

IntT FindAndReplaceString(F_ObjHandleT docId,
                          F_ObjHandleT flowId,
                          StringT findString,
                          StringT replaceString,
                          IntT considerCase)
{
  F_TextRangeT tr,
    restoreTR;

  F_ObjHandleT frameId = 0;

  IntT loopCounter = 0,
    replacementCounter = 0;

  F_PropValsT findParams;

  //if the flow ID is null, assume the main flow
  if(docId && !flowId)
    flowId = F_ApiGetId(FV_SessionId, docId, FP_MainFlowInDoc);

  //get the first text frame in the flow, a starting point to
  //find the first paragraph
  if(flowId)
    frameId = F_ApiGetId(docId, flowId, FP_FirstTextFrameInFlow);

  //At this point, if we don't have a frame ID, might as well abort.
  if(!frameId)
  {
    F_ApiAlert("Could not find a starting point for the search. Cannot continue.",
      FF_ALERT_CONTINUE_WARN);
    return replacementCounter;
  }

  //store the original text selection as an amenity to restore after the action.
  restoreTR = F_ApiGetTextRange(FV_SessionId, docId, FP_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.objId = tr.end.objId = F_ApiGetId(docId, frameId, FP_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 = F_ApiAllocatePropVals(2);

  findParams.val[0].propIdent.num = FS_FindText;
  findParams.val[0].propVal.valType = FT_String;
  findParams.val[0].propVal.u.sval = F_StrCopyString(findString);

  findParams.val[1].propIdent.num = FS_FindCustomizationFlags;
  findParams.val[1].propVal.valType = FT_Integer;
  if(considerCase)
    findParams.val[1].propVal.u.ival = FF_FIND_CONSIDER_CASE;
  else
    findParams.val[1].propVal.u.ival = 0;;

  //initialize the errno global, which will be used to
  //track the progress of the find and replace
  FA_errno = FE_Success;

  //and do an initial find to get started.
  tr = F_ApiFind(docId, &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 == FE_Success && loopCounter++ < 10000)
  {
    //set up the text range to clear the original text
    F_ApiSetTextRange(FV_SessionId, docId, FP_TextSelection, &tr);

    //clear it
    F_ApiClear(docId, 0);

    //insert the new text. We should be able to use the
    //original beginning of the text range where the old text was
    //found.
    F_ApiAddText(docId, &tr.beg, replaceString);

    //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 += F_StrLen(replaceString);

    //increment our return counter
    if(FA_errno == FE_Success) replacementCounter++;

    //...and find the next instance. We'll reset FA_errno again just in case
    //something screwy happened while we were replacing text.
    FA_errno = FE_Success;
    tr = F_ApiFind(docId, &tr.beg, &findParams);
  }

  //done with this.
  F_ApiDeallocatePropVals(&findParams);

  //we're done. Restore the document to it's original area of display
  F_ApiSetTextRange(FV_SessionId, docId, FP_TextSelection, &restoreTR);
  F_ApiScrollToText(docId, &restoreTR);


  return replacementCounter;

}

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
New Here ,
Aug 27, 2011 Aug 27, 2011

Copy link to clipboard

Copied

Rus - that's awesome - thanks....  That is a few more than a couple of lines tho...

Not sure I would have got there on my own.

Jim

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
Mentor ,
Aug 27, 2011 Aug 27, 2011

Copy link to clipboard

Copied

Jim,

Text manipulation turns out to be one of the more complicated things to do with the API, something which is one of the easiest things to do in the GUI. It takes some complexity to replicate what you can easily do with eyes, a screen, and a mouse. Once you get your mind around how to operate and manipulate the TextRangeT structure, though, it becomes much easier. Let me know if any part is confusing and/or you need further clarification.

Russ

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
New Here ,
Aug 28, 2011 Aug 28, 2011

Copy link to clipboard

Copied

Thanks Russ the well commented code makes it obvious - should be easy to translate into ExtendScript.

This is a big help.

Jim

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
Enthusiast ,
Aug 28, 2011 Aug 28, 2011

Copy link to clipboard

Copied

Here's a version of Russ's find/change function in ExtendScript. It may be useful to see the differences.

-Ian

function FindAndReplaceString(activeDoc, flow, findString, replaceString, considerCase) {

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(activeDoc.ObjectValid() && !flow.ObjectValid()){
flow = activeDoc.MainFlowInDoc;
}

//get the first text frame in the flow, a starting point to
//find the first paragraph
if(flow.ObjectValid()){
frame = flow.FirstTextFrameInFlow;
}

//At this point, if we don't have a frame object, might as well abort.
if(!frame.ObjectValid()){
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 = activeDoc.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;
if(considerCase){
findParams[1].propVal.ival = Constants.FF_FIND_CONSIDER_CASE;
}
else{
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 = activeDoc.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
activeDoc.TextSelection = tr;

//clear it
activeDoc.Clear(0);

//insert the new text. We should be able to use the
//original beginning of the text range where the old text was
//found.
activeDoc.AddText(tr.beg, replaceString);

//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 += replaceString.length;

//increment our return counter
if(FA_errno === Constants.FE_Success){
replacementCounter++;
}

//...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 = activeDoc.Find(tr.beg, findParams);
}

//we're done. Restore the document to it's original area of display
activeDoc.TextSelection = restoreTR;
activeDoc.ScrollToText(restoreTR);

return replacementCounter;
}

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
Contributor ,
Sep 21, 2011 Sep 21, 2011

Copy link to clipboard

Copied

Hi Ian, I was wondering whether this script could be adopted to do a search for text string with a change for a variable definition?

Mark

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
Advocate ,
Sep 21, 2011 Sep 21, 2011

Copy link to clipboard

Copied

Mark,

This seems to be pretty easy, although it took me a few moments to find the correct command. Untested:

Replace the line

activeDoc.AddText(tr.beg, replaceString);

with

activeDoc.NewAnchoredFormattedVar('NAME_OF_VARIABLE_FORMAT', tr.beg);

Try it,

- Michael

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
Enthusiast ,
Sep 21, 2011 Sep 21, 2011

Copy link to clipboard

Copied

Hi Mark,

Yes that's easy to do:

Replace the line: activeDoc.AddText(tr.beg, replaceString);


with

var newVar = activeDoc.NewAnchoredFormattedVar('Current Date (Long)', tr.beg);

var varLength = newVar.TextRange.end.offset - newVar.TextRange.beg.offset;

then replace the line: tr.beg.offset += replaceString.length;

with

tr.beg.offset += varLength;

In this example I have hard coded the fm variable format name, but it would be better to pass it into the function as a variable.

Ian

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
Contributor ,
Sep 21, 2011 Sep 21, 2011

Copy link to clipboard

Copied

Thanks Ian/Michael, I'll try this out tomorrow.

Many thanks

Mark

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
Contributor ,
Sep 22, 2011 Sep 22, 2011

Copy link to clipboard

Copied

Must be doing something wrong, but when I run the script from the ExtendScript editor , I get 'Result: undefined' in the console.

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
Advocate ,
Sep 22, 2011 Sep 22, 2011

Copy link to clipboard

Copied

Mark,

This is not relevant, this is the return value to the ExtendScript shell (if it may be called like this). It is rather good news, as there seems to be no syntax or run-time error.

The main question is: Did it work in the document?

- Michael

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
Contributor ,
Sep 22, 2011 Sep 22, 2011

Copy link to clipboard

Copied

Hi Michael,

No it didn't work in the document. I even opened a new doc and imported the Variables from an existing one, to see if that was causing anything (conditional text etc), but it doesn't work on either.

Mark

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
Enthusiast ,
Sep 22, 2011 Sep 22, 2011

Copy link to clipboard

Copied

Hi Mark,

How re you calling the function? Do you have a line such as the following?

FindAndReplaceString(activeDoc, flow, findString, replaceObject, considerCase);


Each of the arguments must be defined before you call the function. In this case the replaceObject is the name of the fm Variable type (the equivalent change would need to be made in the body of the function). Also as this function is being called directly, without being assigned to a variable it will show the return value in the ESTK JavaScript console on completion.

I have tested the revised version and it works as expected.

Ian

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
Contributor ,
Sep 22, 2011 Sep 22, 2011

Copy link to clipboard

Copied

Hi Ian,

Ok, perhaps I'm not understanding how the script works (a bit new to this!)

I wondered whether the replaceString in the original version was needed, being as you removed the reference to it when referencing the Variable name.

I think where I'm stuck is setting up the find parameter for the text I need to replace (looking at your original for text find and replace, I couldn't work that out)

Thanks for being patient with me!!

Mark

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
Enthusiast ,
Sep 22, 2011 Sep 22, 2011

Copy link to clipboard

Copied

No problem Mark,

As this is an example function, rather than a complete working script, some parts were not explained. For maximum flexibility you would probably want this function to handle any type of replace object. However, to get this working let's start with the arguments for the function. You will need to define these:

var activeDoc = app.ActiveDoc;

var flow = activeDoc.MainFlowInDoc;

var findString = 'Your search text';

var replaceObject = 'YourVariableName';

var considerCase = 0;

In the body of the function make sure that the replaceObject is used:

var newVar = activeDoc.NewAnchoredFormattedVar(replaceObject, tr.beg);

Don't worry about needing more detail, we all have to start somewhere. The only thing I would say is to start a new thread next time as we are moving away from the original topic.

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
Advocate ,
Nov 17, 2011 Nov 17, 2011

Copy link to clipboard

Copied

LATEST

Ian,

It seems the constants for the FS_FindCustomizationFlags are not defined in ExtendScript:

Constants.FF_FIND_CONSIDER_CASE (0x01)

Constants.FF_FIND_WHOLE_WORD (0x02)

Constants.FF_FIND_USE_WILDCARDS (0x04)

Constants.FF_FIND_BACKWARDS (0x08)

So if you want to combine Consider Case and Whole Word, you would have to write

findParams[1].propIdent.num = Constants.FS_FindCustomizationFlags;

findParams[1].propVal.valType = Constants.FT_Integer;

findParams[1].propVal.ival = 1 | 2;

BTW, I find the full property list handling awful complidated… and this is an example where FrameScript is a lot easier and less error-prone.

- Michael

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 ,
Oct 09, 2011 Oct 09, 2011

Copy link to clipboard

Copied

Jim, do you know the features of the Finalyser to find/replace formats, element definitions, words, phrases, find index and brand new conditional text with conditional format.

All is to use in a document or in a book. It's really great.

- Georg

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