Highlighted

Replace string with formatted text

Advocate ,
Jan 27, 2016

Copy link to clipboard

Copied

Dear all - I'm back to my beloved project...

I want to replace a found string by a TextSelction (formatted text) and started with Jang’s famous function  FindAndReplaceString.
Since my replacement comes from a different document (sourceDoc)  I edited activeDoc to targetDoc and introduced a second document (sourceDoc).
Actually the replacePara comes from an arry where it had been placed to avoid switching back and forth between the documents of a book (where to find and replace) and the source documents. I have learnt in another function that the information on the array requires that the sourceDoc must stay open.

  • Of course everything works fine until I want to insert the replacelement:
    line 26 clears the found string
  • Since I do not insert a string, I skip lines 28 and 29 and try try line 30
  • At line 30 sourceDoc is object Document and replacePare is object TextSelection. However, sourceDoc.replacePara is undefined and
  • (as a consequence ?) line 31 pasts the current clipboard contents.

Obviously there is some fog around me ... and I need some sunshine.

function FindAndReplacePara (targetDoc, findString, sourceDoc, replacePara, loopMax) {
  var tr = new TextRange();
  var restoreTR, frame = 0, loopCounter = 0, replacementCounter = 0;
  var findParams = new PropVals();
  var firstPgf = targetDoc.MainFlowInDoc.FirstTextFrameInFlow.FirstPgf;
 
  tr.beg.obj = tr.end.obj = firstPgf;             //  set up the starting text range as the very beginning
  tr.beg.offset = tr.end.offset = 0;              // of the flow. We'll move straight from beginning to end.
  trSaved = tr                                    // to come back after work

  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;
  findParams[1].propVal.ival = Constants.FF_FIND_CONSIDER_CASE;

  FA_errno = Constants.FE_Success;                // errno global, to be used to track the progress of the find and replace
  tr = targetDoc.Find(tr.beg, findParams);        // and do an initial find to get started.
 
  while(FA_errno === Constants.FE_Success && loopCounter++ < 2*loopMax) { //find and replace loop as long as we keep finding
    targetDoc.TextSelection = tr;                 // set up the text range to clear the original text
    targetDoc.Clear(0);                           // clear it
   
//    targetDoc.AddText(tr.beg, replacePara);       // insert the new text at the original beginning of the text range
//    tr.beg.offset += replacePara.length;          //  lets jimmy the text range in memory to place it directly after
    targetDoc.TextSelection = sourceDoc.replacePara;        // paste the whole replacement paragraph
    targetDoc.Paste (0);                          // <-- Current contents of clipboard is pasted !!!!
    if(FA_errno === Constants.FE_Success) {       // increment our return counter
      replacementCounter++;
    }
    FA_errno = Constants.FE_Success;              // ...  find the next instance. We'll reset FA_errno again just in case
    tr = targetDoc.Find(tr.beg, findParams);      // something screwy happened while we were replacing text.
  }
  targetDoc.ScrollToText(trSaved);                // we're done. Restore the document to it's original area of display
  return replacementCounter;
} // --- end FindAndReplacePara

Hi Klaus,

Thanks for the explanation. I remember that GetTabRange stuff from way back. You have been quite patient and persistent with this project, indeed.

So, given that we are dealing with text ranges here, I think that the only simple approach is to perform a copy/paste. There is no way to save a "formatted string" in a variable. The only super-precise way would be to get a TextItems data structure from each text range in the sourceDoc, then meticulously recreate each item in the targetDoc. This would be wildly complicated, I think, so a copy/paste seems the most logical. I'm wondering, though, if I'm missing something, because I think maybe you would have tried it already if so (?)

Anyway, I would do something like this, starting at line 30 of your original code sample (untested code here):

app.ActiveDoc = sourceDoc;

sourceDoc.TextSelection = replacePara;

sourceDoc.Copy(0);

app.ActiveDoc = targetDoc;

targetDoc.Paste(0);

... etc.

Does this make sense, or am I barking up the wrong tree, as they say?

Russ

TOPICS
Scripting

Views

1.2K

Likes

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

Replace string with formatted text

Advocate ,
Jan 27, 2016

Copy link to clipboard

Copied

Dear all - I'm back to my beloved project...

I want to replace a found string by a TextSelction (formatted text) and started with Jang’s famous function  FindAndReplaceString.
Since my replacement comes from a different document (sourceDoc)  I edited activeDoc to targetDoc and introduced a second document (sourceDoc).
Actually the replacePara comes from an arry where it had been placed to avoid switching back and forth between the documents of a book (where to find and replace) and the source documents. I have learnt in another function that the information on the array requires that the sourceDoc must stay open.

  • Of course everything works fine until I want to insert the replacelement:
    line 26 clears the found string
  • Since I do not insert a string, I skip lines 28 and 29 and try try line 30
  • At line 30 sourceDoc is object Document and replacePare is object TextSelection. However, sourceDoc.replacePara is undefined and
  • (as a consequence ?) line 31 pasts the current clipboard contents.

Obviously there is some fog around me ... and I need some sunshine.

function FindAndReplacePara (targetDoc, findString, sourceDoc, replacePara, loopMax) {
  var tr = new TextRange();
  var restoreTR, frame = 0, loopCounter = 0, replacementCounter = 0;
  var findParams = new PropVals();
  var firstPgf = targetDoc.MainFlowInDoc.FirstTextFrameInFlow.FirstPgf;
 
  tr.beg.obj = tr.end.obj = firstPgf;             //  set up the starting text range as the very beginning
  tr.beg.offset = tr.end.offset = 0;              // of the flow. We'll move straight from beginning to end.
  trSaved = tr                                    // to come back after work

  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;
  findParams[1].propVal.ival = Constants.FF_FIND_CONSIDER_CASE;

  FA_errno = Constants.FE_Success;                // errno global, to be used to track the progress of the find and replace
  tr = targetDoc.Find(tr.beg, findParams);        // and do an initial find to get started.
 
  while(FA_errno === Constants.FE_Success && loopCounter++ < 2*loopMax) { //find and replace loop as long as we keep finding
    targetDoc.TextSelection = tr;                 // set up the text range to clear the original text
    targetDoc.Clear(0);                           // clear it
   
//    targetDoc.AddText(tr.beg, replacePara);       // insert the new text at the original beginning of the text range
//    tr.beg.offset += replacePara.length;          //  lets jimmy the text range in memory to place it directly after
    targetDoc.TextSelection = sourceDoc.replacePara;        // paste the whole replacement paragraph
    targetDoc.Paste (0);                          // <-- Current contents of clipboard is pasted !!!!
    if(FA_errno === Constants.FE_Success) {       // increment our return counter
      replacementCounter++;
    }
    FA_errno = Constants.FE_Success;              // ...  find the next instance. We'll reset FA_errno again just in case
    tr = targetDoc.Find(tr.beg, findParams);      // something screwy happened while we were replacing text.
  }
  targetDoc.ScrollToText(trSaved);                // we're done. Restore the document to it's original area of display
  return replacementCounter;
} // --- end FindAndReplacePara

Hi Klaus,

Thanks for the explanation. I remember that GetTabRange stuff from way back. You have been quite patient and persistent with this project, indeed.

So, given that we are dealing with text ranges here, I think that the only simple approach is to perform a copy/paste. There is no way to save a "formatted string" in a variable. The only super-precise way would be to get a TextItems data structure from each text range in the sourceDoc, then meticulously recreate each item in the targetDoc. This would be wildly complicated, I think, so a copy/paste seems the most logical. I'm wondering, though, if I'm missing something, because I think maybe you would have tried it already if so (?)

Anyway, I would do something like this, starting at line 30 of your original code sample (untested code here):

app.ActiveDoc = sourceDoc;

sourceDoc.TextSelection = replacePara;

sourceDoc.Copy(0);

app.ActiveDoc = targetDoc;

targetDoc.Paste(0);

... etc.

Does this make sense, or am I barking up the wrong tree, as they say?

Russ

TOPICS
Scripting

Views

1.2K

Likes

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
Jan 27, 2016 0
Mentor ,
Jan 27, 2016

Copy link to clipboard

Copied

Hi Klaus,

I see dense fog around line 30   As best I can tell, this is a completely invalid statement:

targetDoc.TextSelection = sourceDoc.replacePara;

A text selection is a data structure that consists of a start point and an end point, where each point consists of a paragraph object and an offset. These objects must be members of the parent document itself. It doesn't make any sense to assign paragraph objects from another document to a text range. It would be like me giving out directions to the US White House, using a map of Moscow. No logical correlation anywhere in that. Furthermore, like I mentioned, a text range is a data structure with multiple objects. So it doesn't make sense to assign a single paragraph object to it. Furtherfurthermore, "sourceDoc.replacePara" is an invalid expression by itself, because there is no "replacePara" property of a document object. These are two separate variables passed in the function arguments.

You did mention that "replacePara" is a "TextSelection" object, so maybe my second point is invalid. But I'm not sure... your variable name suggests that it is a paragraph object. Also, there is no such thing as a TextSelection object, perhaps you meant a TextRange object? In any case, I am sure that points 1 and 3 are correct.

Anyway, now that I've sufficiently trashed line 30, perhaps you could elaborate on what you really want to do. Are you simply trying to copy a paragraph (or part of a paragraph) from sourceDoc into the Find locations in targetDoc? And could you clarify what kind of variable "replacePara" really is?

I think we can get this to work with minimal effort... I just see some need for clarification.

Russ

Likes

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
Reply
Loading...
Jan 27, 2016 0
Advocate ,
Jan 27, 2016

Copy link to clipboard

Copied

Thanks Russ for the answer - so I stepped back and first tried to just paste the data as string. This works perfectly thanks the function GetText (by Rick) which I use frequently in my script.

The relevant part in the script now is this:

  targetDoc.TextSelection = tr;                 // set up the text range to clear the original text
  targetDoc.Clear(0);                           // clear it
  replaceString = GetText (replacePara, sourceDoc); // expects TextObject, Doc
  targetDoc.AddText(tr.beg, replaceString);     // insert the new text at the original beginning of the text range
  tr.beg.offset += replaceString.length;        // lets jimmy the text range in memory to place it directly after

But how can I insert the formatted text which is in the variable replacePara? replacePara is (watch the italic portion):

Müller, P. D. H. (1925). De viris illustribus, Carl Meyer (Gustav Prior), Hannover.

BTW: know what 'to jimmy' is: apply some brute force. IMHO special words protect against plagiarism...

I always keep the sources mentioned. In this functions it is

// Source: script by Russ Ward in https://forums.adobe.com/message/3888653#3888653

Likes

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
Reply
Loading...
Jan 27, 2016 0
Mentor ,
Jan 27, 2016

Copy link to clipboard

Copied

Klaus, I got an email from the forum with a post from you, but I don't see it here on the web page. I'll answer it anyway.

I can't figure out what you mean by "formatted text which is in the variable replacePara". What is "formatted text"? A variable cannot contain formatted text, at least the way you seem to be thinking. I need to know how you are setting replacePara and what type of variable it is.

My suspicion is that you just want to copy from the source document and paste to the target document. In that case, you need to set the text selection in the source doc, call sourceDoc.Copy() (with the source doc active, probably), activate the target doc, then targetDoc.Paste().

And by the way, "jimmy" is an excellent plagiarism detector. That's how I know the source code was originally from me. If Jang tried to swipe it, well, that dirty rat, totally busted.

Russ

Likes

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
Reply
Loading...
Jan 27, 2016 0
Advocate ,
Jan 28, 2016

Copy link to clipboard

Copied

Russ, the explanation of variable replacePara is somewhat lenghty:

«My suspicion is that you just want to copy from the source document and paste to the target document. In that case, you need to set the text selection in the source doc, call sourceDoc.Copy() (with the source doc active, probably), activate the target doc, then targetDoc.Paste().» is correct in principle, but...


The pupose of the discussed part of the script is to replace placeholders (such as {Barnes-Ellerbe, 2004 #48}) in targetDoc with complete references (such as Barnes-Ellerbe, S., Knudsen, K. E., and Puga, A. (2004). "2,3,7,8-Tetrachlorodibenzo-p-dioxin blocks androgen-dependent cell proliferation of LNCaP cells through modulation of pRB phosphorylation." Mol Pharmacol, 66(3), 502-11.) from a sourceDoc (note the italic part in this text - this i call formatted text, ince the whole thing can not be treated just as string).
Since the placeholders are located in various documents of a book, the information from the document with the replacement texts (sourceDoc ) is filled into arrays. Otherwise I would need to switch between the targetDoc/oDoc2 and sourceDoc/oDoc1 for each reference in the sourceDoc. BTW this sourceDoc is an RTF created by a bibliographic application (EndNote or Citavi) and opened in FM, hence a standard fm document.
I have 3 global arrays:

var gasFmtCitsRaw  = [];                        // left column in processed RTF
var gasFmtCitsFmt  = [];                        // right column in processed RTF
var gaoBibliography = [];                        // bibliography paragraphs from processed RTF


They are filled in function GetBiblioFromRTF with the following statements relevant for replacePara:

// --- read the bibliography paragraphs
  while (pgf.ObjectValid()) {                    // until end of doc
    tabRange = GetTabRange (pgf, goRtfDoc);      // Get the text range starting after the TAB to the end of the paragraph.
    if (tabRange) {
    gaoBibliography.push(tabRange);              // store the paragraph-part in the array
    pgf = pgf.NextPgfInFlow;
    } else { break }                              // We are out of the bibliography range
  }


Then in a loop the find/replace operation is performed:

function ExpandInDoc (oDoc2, oDoc1) {
// oDoc2 target document where to replace
// oDoc1 source document of the replacements in the array
  var j, jBiblio, nBiblio, nCitations, findString, replacePara, loopMax, overallCounter = 0;
  var nCitations = gasFmtCitsRaw.length;
  var loopMax = nCitations;
  for (j = 0; j < nCitations; j += 1) {
    findString    = gasFmtCitsRaw;
    jBiblio      = gasFmtCitsFmt;            // number of biblio-paragraph
    replacePara = gaoBibliography[jBiblio-1];    // is object TextRange
    replacementCounter = FindAndReplacePara (oDoc2, findString, oDoc1, replacePara, loopMax);
  }
} // --- end ExpandInDoc


IMHO replacePara is a TextRange which contains formatted text (string pieces with character formats forming part of a paragraph).

Then function FindAndReplacePara comes into play.

I hope this will explain the nature of replacePara.

Likes

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
Reply
Loading...
Jan 28, 2016 0
Mentor ,
Jan 28, 2016

Copy link to clipboard

Copied

Hi Klaus,

Thanks for the explanation. I remember that GetTabRange stuff from way back. You have been quite patient and persistent with this project, indeed.

So, given that we are dealing with text ranges here, I think that the only simple approach is to perform a copy/paste. There is no way to save a "formatted string" in a variable. The only super-precise way would be to get a TextItems data structure from each text range in the sourceDoc, then meticulously recreate each item in the targetDoc. This would be wildly complicated, I think, so a copy/paste seems the most logical. I'm wondering, though, if I'm missing something, because I think maybe you would have tried it already if so (?)

Anyway, I would do something like this, starting at line 30 of your original code sample (untested code here):

app.ActiveDoc = sourceDoc;

sourceDoc.TextSelection = replacePara;

sourceDoc.Copy(0);

app.ActiveDoc = targetDoc;

targetDoc.Paste(0);

... etc.

Does this make sense, or am I barking up the wrong tree, as they say?

Russ

Likes

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
Reply
Loading...
Jan 28, 2016 0
Advocate ,
Jan 29, 2016

Copy link to clipboard

Copied

Wow! Russ, that's a real break-trhough!

This does exactly what I want to achieve!

Now I see the end of the tunnel for my project.

I had experimented with copy and paste but obviously did not get it right - my knowledge of the object model still is in anfant state...

Likes

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
Reply
Loading...
Jan 29, 2016 0
Mentor ,
Jan 29, 2016

Copy link to clipboard

Copied

Klaus, I'm happy to hear that. Regarding the copy/paste, it is my assumption that you have to make the source and target documents active before you do the copy and paste, respectively. Even though the Copy() and Paste() methods are assigned to the document object, I don't believe that they will just work independently. In the FDK, they were "global" functions that didn't need any document object... it was up to you to make whichever document active, just like working in the GUI. Anyway, the documentation doesn't say this anywhere, just my guess. I'd be interested to know if you could validate that.

Russ

Likes

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
Reply
Loading...
Jan 29, 2016 0
Advocate ,
Jan 30, 2016

Copy link to clipboard

Copied

Russ, it works perfectly, as you can see from this Captivate Flash.

What I have noted after that captureing is this: Although the ¶ format of the Footnote in the target file has no * in the information area, the format is not correct. Re-applying format Footnote lets the size shrink. Only the 'not expanded' footnotes starting with a { keep their format.

But this can be fixed by re-applying the format of the footnote.

The reason propably is that in the sourceDoc (coming from an RTF generated by the biblogrphic app) uses a format named HeadingRunIn with different properties. The effect will be even more visible, if I have a targetDoc with sans-serif styles...

Likes

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
Reply
Loading...
Jan 30, 2016 0
Enthusiast ,
Feb 16, 2016

Copy link to clipboard

Copied

FA_errno = Constants.FE_Success;                // errno global, to be used to track the progress of the find and rep

Hi Klaus,

FA_errno is write-protected. This should result in an error message.

Likes

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
Reply
Loading...
Feb 16, 2016 0
Mentor ,
Feb 16, 2016

Copy link to clipboard

Copied

Hi Klaus and Klaus,

It is true (and unfortunate) that FA_errno is write-protected. With the FDK, it was common practice to reset it in certain circumstances. Nonetheless, there are tricks to do the same thing with ExtendScript. Here is a thread that shows one:

Are there situations where you can NOT override the FA_errno's code?

Russ

Likes

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
Reply
Loading...
Feb 16, 2016 0
Enthusiast ,
Feb 16, 2016

Copy link to clipboard

Copied

Hi Russ,

thanks for this link.

This is exactly what I am doing. I use :

var openParams = GetOpenDefaultParams();

It results in FA_errno = 0 and works fine.

Likes

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
Reply
Loading...
Feb 16, 2016 0