Copy link to clipboard
Copied
I'm pasting several Pgf objects at once into a file (a changes file of sections containing changebars), in the main text flow. I want a divider line to go at the end of each pasted section, so I try to insert one after the paste operation. But instead of a line between each section, all lines are added at the end of the file, after all the sections. So if there are 3 pasted sections, at the end of the file are three lines. My code is below, do you know how I might remedy this?
Thanks, Mark
function copyPasteChanges(doc, chgRange) {
// Select section with changebar and copy it.
doc.TextSelection = chgRange;
doc.Copy();
// Get the changes doc ready and paste the changed section into it.
var firstChgPgf = changesDoc.MainFlowInDoc.FirstTextFrameInFlow.LastPgf;
var changesTloc = new TextLoc(firstChgPgf, Constants.FV_OBJ_END_OFFSET);
var changesTRange = new TextRange(changesTloc, changesTloc);
changesDoc.TextSelection = changesTRange;
changesDoc.Paste();
// Get to end of file again (I think)
var lastPgf = changesDoc.MainFlowInDoc.FirstTextFrameInFlow.LastPgf;
var dividerPgf = changesDoc.NewSeriesPgf ( lastPgf );
var TLoc = new TextLoc ( dividerPgf, 0 );
changesDoc.AddText ( TLoc, "_______________________" );
}
Hi Mark,
The problem is that the paste is not occurring where you think. The divider line is being added at the end of the file, but your paste actions are not. In fact, I think it may be somewhat random where the pastes are occurring, because this line:
var changesTloc = new TextLoc(firstChgPgf, Constants.FV_OBJ_END_OFFSET);
...is not creating a valid text location. You can confirm this by aborting the function after:
changesDoc.TextSelection = changesTRange;
...and looking at where the inse
...Copy link to clipboard
Copied
Hi Mark,
The problem is that the paste is not occurring where you think. The divider line is being added at the end of the file, but your paste actions are not. In fact, I think it may be somewhat random where the pastes are occurring, because this line:
var changesTloc = new TextLoc(firstChgPgf, Constants.FV_OBJ_END_OFFSET);
...is not creating a valid text location. You can confirm this by aborting the function after:
changesDoc.TextSelection = changesTRange;
...and looking at where the insertion point is. It will probably be wherever you left it last, because the call is invalid. I can't tell you exactly why, except that it has something to do with the fact that you are using the last paragraph in the flow. There is something weird about trying to set a full-paragraph text range with the last paragraph. I'm sure it is related to how the last pgf in a flow does not show a pgf mark, but beyond that, I don't know how to do what you are trying to do with the last paragraph.
Lacking the knowledge of how to do it correctly, any time I need to do this, I create a "dummy" paragraph at the end of the flow to fill in that strange "last pgf" space, then do the work around it. At the end, I just delete the dummy pgf. I don't know if this is best practice or not, but it's the only way I ever get it to work and it does sort of mirror how you might do it manually in the GUI.
With that, I modified your script as follows and it seems to work better. Note that I also modified the logic of how to find the last paragraph of the flow by writing a separate function. Your call to "FirstTextFrameInFlow" is OK unless your doc starts to span multiple pages and/or begins with multiple empty pages. The function I wrote ensures that you truly have the last pgf in the flow.
I hope this helps.
Russ
function copyPasteChanges(doc, chgRange) {
// Select section with changebar and copy it.
doc.TextSelection = chgRange;
doc.Copy();
// Get the changes doc ready and paste the changed section into it.
var lastPgf = getLastPgf(changesDoc);
var dummyPgf = changesDoc.NewSeriesPgf (lastPgf);
var changesTloc = new TextLoc(dummyPgf, 0);
var changesTRange = new TextRange(changesTloc, changesTloc);
changesDoc.TextSelection = changesTRange;
changesDoc.Paste();
// Get to end of file again (I think)
//Don't forget that the last pgf is now the dummy pgf
lastPgf = getLastPgf(changesDoc);
var dividerPgf = changesDoc.NewSeriesPgf (lastPgf.PrevPgfInFlow);
var TLoc = new TextLoc ( dividerPgf, 0 );
changesDoc.AddText ( TLoc, "_______________________" );
dummyPgf.Delete();
}
function getLastPgf(doc)
{
var textFrame = doc.MainFlowInDoc.LastTextFrameInFlow;
var lastPgf = textFrame.LastPgf;
while(!lastPgf.ObjectValid() && textFrame.ObjectValid())
{
textFrame = textFrame.PrevTextFrameInFlow;
lastPgf = textFrame.LastPgf;
}
return lastPgf;
}
Copy link to clipboard
Copied
Thanks Russ, this is great stuff. It solved my problem. And tipping me off that weird things happen at the last paragraph of a flow also helped me to fix another problem I was having, where I was trying to select the end of a section at the end of a file, with no following heading to define the end. Who says dummies aren't useful! (dummy Pgfs!)
Your code in getLastPgf() seems to imply that what the API gives you as the last Pgf (textFrame.LastPgf) might not actually be the last pgf, and so you have to slap it around to make it tell you something more useful. Is that correct? You get the last pgf, but then you check it to see if it's valid.
Copy link to clipboard
Copied
Hi Mark,
I'm glad that I could help. Regarding your question, no that is not correct. textFrame.LastPgf will reliably give you the last pgf in a text frame. The tricky thing is that each page has its own text frame, therefore a multi-page flow will have multiple text frames. If you keep asking for FirstTextFrameInFlow.LastPgf, you'll only ever get the last paragraph on the first page. That works fine until your content grows to multiple pages, then you start pasting stuff at the bottom of page 1 every time. The reason that the getLastPgf() function starts with the last frame in the flow is that it is the easiest reliable way to find the last pgf. You might have trailing pages with no content at all, and thus no paragraphs. Therefore, it works to walk backwards and stop when you hit a valid lastPgf, then you know you got the last one in the flow.
Russ
Copy link to clipboard
Copied
Postscript... it's interesting to me that the interface doesn't have a flow.firstPgf or flow.lastPgf property, but it does have pgf.NextPgfInFlow, etc. Or, at least the FDK and ES documentation doesn't list them. You can presumably call GetText() on a flow and get paragraph objects, but that seems like expensive overkill just to get a single paragraph.
Russ
Copy link to clipboard
Copied
Hi Russ, you are right. FrameScript has Flow.FirstPgfInFlow and Flow.LastPgfInFlow properties. But apparently these are just FrameScript "shortcuts". Other FrameScript properties that are useful: .Text gives you the text of a text object (Pgf, TextFrame, Flow, etc.) and .Properties gives you a list of the object's properties.
Copy link to clipboard
Copied
I see the flaw in my FirstTextFrameInFlow.LastPgf.
And in asking for the last text frame and last Pgf in that frame, just because you tell The FrameMaker Product to give you them, it does not mean you actually get the last Pgf:
var textFrame = doc.MainFlowInDoc.LastTextFrameInFlow;
var lastPgf = textFrame.LastPgf;
So you can depend on getting the last text frame. But you can't depend on there being a Pgf in that last text frame, because it might be an empty frame. So you have to verify it and if it's invalid (not there) step back to the previous text frame and check for the last Pgf again, and so on:
while(!lastPgf.ObjectValid() && textFrame.ObjectValid())
{
textFrame = textFrame.PrevTextFrameInFlow;
lastPgf = textFrame.LastPgf;
}
And then, to really be able to grab that last paragraph once you've chased it down, you need to use the Russ Method on it and add a dummy paragraph. I got it now!
Seems like The FrameMaker Product has an ambiguous state of mind about last paragraphs.