Copy link to clipboard
Copied
I’m trying to bring this function alive:
$.writeln( findPagesOfLastCells() );
function findPagesOfLastCells() {
// https://community.adobe.com/t5/indesign-discussions/javascript-property-to-tell-if-text-is-in-a-continued-table/td-p/6379093
var aDoc = app.activeDocument,
allTables = aDoc.stories.everyItem().tables,
thePages = [];
if( allTables.length > 0 ) {
for ( var i = 0; i < allTables.length; i++ ) {
var theTable = allTables[i];
$.writeln('***********tables************\n'+theTable);
if ( theTable.rows.length > 1 ) {
var lastRowPage = 0;
lastRowPage = theTable.rows[-1].cells[-1].insertionPoints[-1].parentTextFrames[0].parentPage;
$.writeln(lastRowPage);
thePages.push(lastRowPage.name);
$.writeln(lastRowPage.name);
}
}
}
return thePages;
}
The crucial parts is here:
lastRowPage = theTable.rows[-1].cells[-1].insertionPoints[-1].parentTextFrames[0].parentPage;
This doesn’t work and results to undefined. It works fine with this:
theTable.rows[-1].cells[-1].insertionPoints[-1]
I get an insertion point with all properties, including a parentTextFrames property. But extending to
theTable.rows[-1].cells[-1].insertionPoints[-1].parentTextFrames[0]
I get a text frame reference with no property at all.
Is this a bug? Am I doing something wrong?
Thanks for your kind help,
Tobias
Uwe, this is pretty nasty, puh – thank you very much for pointing this out!
So for completeness sake:
$.writeln( findPagesOfLastCells() );
function findPagesOfLastCells() {
// https://community.adobe.com/t5/indesign-discussions/javascript-property-to-tell-if-text-is-in-a-continued-table/td-p/6379093
var aDoc = app.activeDocument,
allTables = aDoc.stories.everyItem().tables,
thePages = [];
if( allTables.length > 0 ) {
for ( var i = 0; i < allTables.length; i++ ) {
...
Hi @tobias.wantzen , Uwe used the table storyOffset property answering another post this morning, and it seems like it might work here. You can get a table‘s trailing insertionPoint to find which page it ends on. Something like this:
var et = app.activeDocument.stories.everyItem().tables.everyItem().getElements();
alert(findPagesOfLastCells(et))
/**
* Gets the parent page of a table’s end
* @ param a collection of tables
* @ return an array of page names
*
*/
function findPagesOfLastCell
...
Copy link to clipboard
Copied
An insertion point in a cell has no parent text frame. Tables are weird in how they interact with the DOM like that. You have to get the text frame via other means, but I forget specifically how I've done it in the past. I just know it wasn't trivial. Maybe someone else has a better, easier implementation.
Copy link to clipboard
Copied
Thanks, @brian_p_dts for your kind answer. Meanwhile I’ve done some nasty bit of code. It works, but – puh! – it’s very hacky and ugly (because intrusive):
$.writeln( findPagesOfLastCells() );
function findPagesOfLastCells() {
// https://community.adobe.com/t5/indesign-discussions/javascript-property-to-tell-if-text-is-in-a-continued-table/td-p/6379093
var aDoc = app.activeDocument,
allTables = aDoc.stories.everyItem().tables,
thePages = [];
if( allTables.length > 0 ) {
for ( var i = 0; i < allTables.length; i++ ) {
var theTable = allTables[i];
$.writeln('***********tables************\n'+theTable);
if ( theTable.rows.length > 1 ) {
var lastRowPage = 0;
var lastRowPage = getAOsParentPage( theTable.rows[-1].cells[-1].insertionPoints[-1] );
$.writeln(lastRowPage);
thePages.push(lastRowPage.name);
$.writeln(lastRowPage.name);
}
}
}
return thePages;
}
function getAOsParentPage(theAOReference) {
var aDoc = app.activeDocument;
var theAO = aDoc.rectangles.add({ geometricBounds: [0,0,5,5] });
theAO.anchoredObjectSettings.insertAnchoredObject( theAOReference, AnchorPosition.ANCHORED );
theAO.anchoredObjectSettings.releaseAnchoredObject();
var theAOparentPage = theAO.parentPage;
theAO.remove();
return theAOparentPage;
}
If someone has a better idea, it’ll be very welcome ...
Thanks, Tobias
Copy link to clipboard
Copied
Pretty sure that's along the lines of how I've implemented it before. Yeah, not pretty.
Copy link to clipboard
Copied
Hi Tobias,
I think it will make a big difference when allTables is an array and not a collection of tables:
var allTables =
aDoc.stories.everyItem().tables.everyItem().getElements();
Other things to think about:
Are there perhaps graphic cells?
( I guess not, because your insertion point seems to be valid. )
Is a table perhaps in overset?
Is the last cell of a given row in overset?
Are there any footer rows in the table?
You will get the wrong page as the last one if the table runs through several text frames on different pages.
Regards,
Uwe Laubender
( Adobe Community Professional )
Copy link to clipboard
Copied
My code wasn’t meant to be bullet-proof, but to show, that parentTextFrame doesn’t work in tables and to ask for help on this topic. And my second code showed an approach to deal with that limitation as a workaround.
I’m absolutely with you: To be bullet-proof there have to be much more fine grinding. Thanks for your annotations showing manyfold directions, which has to be taken into account.
But let me ask one question:
> I think it will make a big difference when allTables is an array and not a collection of tables:
Why is using array over object better? Are there any other reasons except performance?
Thanks,
Tobias
Copy link to clipboard
Copied
"Why is using array over object better?"
The elements in that array are resolved. So you can be sure that the target elements actually result in meaningful properties/values if a given situation allows this.
Also read both articles about everyItem() from Marc Autret:
https://www.indiscripts.com/post/2010/06/on-everyitem-part-1
https://www.indiscripts.com/post/2010/07/on-everyitem-part-2
getElements() is a topic of part 2, but don't miss part 1; it's worth the effort!
Quoting Marc from part 2:
"…A common way to explicitly resolve a specifier and push the resolved data into an Array is to use spec.getElements(). This method performs two operations:
(1) It causes an automatic resolution of spec;
(2) It creates and returns an Array that contains each spec's receiver converted into a resolved specifier.
Moreover, getElements() is much more reliable than any other command because it actually resolves or updates the specifier from its original path…"
Regards,
Uwe Laubender
( Adobe Community Professional )
Copy link to clipboard
Copied
Brian: "An insertion point in a cell has no parent text frame. "
Hi Brian,
of course it has.
If that's not the case, the insertion point is in overset text.
Regards,
Uwe Laubender
( Adobe Community Professional )
Copy link to clipboard
Copied
Hi Tobias,
theTable.rows[-1]
is always in the first frame of that table if that row has at least one footer row.
So best change that to:
theTable.rows[ -1-theTable.footerRowCount ]
Regards,
Uwe Laubender
( Adobe Community Professional )
Copy link to clipboard
Copied
Uwe, this is pretty nasty, puh – thank you very much for pointing this out!
So for completeness sake:
$.writeln( findPagesOfLastCells() );
function findPagesOfLastCells() {
// https://community.adobe.com/t5/indesign-discussions/javascript-property-to-tell-if-text-is-in-a-continued-table/td-p/6379093
var aDoc = app.activeDocument,
allTables = aDoc.stories.everyItem().tables,
thePages = [];
if( allTables.length > 0 ) {
for ( var i = 0; i < allTables.length; i++ ) {
var theTable = allTables[i];
if ( theTable.rows.length > 1 ) {
var lastRowPage = 0;
var lastRowPage = getAOsParentPage( theTable.rows[-1-theTable.footerRowCount].cells[-1].insertionPoints[-1] );
thePages.push(lastRowPage.name);
}
}
}
return thePages;
}
function getAOsParentPage(theAOReference) {
var aDoc = app.activeDocument;
var theAO = aDoc.rectangles.add({ geometricBounds: [0,0,5,5] });
theAO.anchoredObjectSettings.insertAnchoredObject( theAOReference, AnchorPosition.ANCHORED );
theAO.anchoredObjectSettings.releaseAnchoredObject();
var theAOparentPage = theAO.parentPage;
theAO.remove();
return theAOparentPage;
}
But please be aware what Uwe pointed out in his first post of all further issues, which may be important for reusing this code in your own context!
Copy link to clipboard
Copied
Hi @tobias.wantzen , Uwe used the table storyOffset property answering another post this morning, and it seems like it might work here. You can get a table‘s trailing insertionPoint to find which page it ends on. Something like this:
var et = app.activeDocument.stories.everyItem().tables.everyItem().getElements();
alert(findPagesOfLastCells(et))
/**
* Gets the parent page of a table’s end
* @ param a collection of tables
* @ return an array of page names
*
*/
function findPagesOfLastCells(t){
var a = new Array()
var ti, psi;
for (var i = 0; i < t.length; i++){
ti = t[i].storyOffset.index;
//the parent page of the insertion point at the end of the table
psi = t[i].parent.parentStory.insertionPoints[ti+1].parentTextFrames[0].parentPage
a.push(psi.name)
};
return a
}
Copy link to clipboard
Copied
Hi Rob,
thank you for this! It's working very well with my test documents.
Your algorithm is superior* to the one where one is looking at the parent page of an insertion point in a table cell of the last body row. If Tobias does not mind I will mark your answer as the correct one.
Thanks,
Uwe Laubender
( Adobe Community Professional )
* EDIT: I have to add a comment and correct myself on this.
Rob's algorithm detects the right page if:
[1] There is no text after the table in the story
[2] If at least one character is rendered in the same text frame with the last row of the table.
But if the first character that comes after the table is rendered in a different text frame on a different page one can see that the position of the insertion point moved along with the character and the wrong page is detected as the last one of the table.
See Rob's reply her in the same discussion:
Copy link to clipboard
Copied
Thanks uwe, I was wondering why there wasn’t an insertionPoint property for tables until you pointed out the storyOffset in the other post.
Copy link to clipboard
Copied
Hi Rob,
I never thought of inspecting the insertion point after the character that is the actual table, because that character is positioned in a different text frame if you have two threaded text frames where one table is running through.
Test this with:
characters[0].parentTextFrames.length // Returns 1
characters[0].parentTextFrames[0].parentPage.name
// Returns the page name of the page at the beginning of the table.
But well, let's test if this still true when there is more text after that table.
Alright. Tested just this and your algorithm is still working as expected:
Above: A table with one header row, one footer row and two body rows that is running through two text frames on different pages.
Regards,
Uwe Laubender
( Adobe Community Professional )
Copy link to clipboard
Copied
I think it works because the storyOffset insertion point is to the left of the table—not above it— and the next insertion point is to the right of the table—not below it.
I can see that if I select the insertionPoints:
The storyOffset selected
var et = app.activeDocument.stories.everyItem().tables.everyItem().getElements();
var ti = et[0].storyOffset.index;
var psi = et[0].parent.parentStory.insertionPoints[ti]
psi.select()
And the next insertionPoint selected:
var et = app.activeDocument.stories.everyItem().tables.everyItem().getElements();
var ti = et[0].storyOffset.index;
var psi = et[0].parent.parentStory.insertionPoints[ti+1]
psi.select()
I don‘t think we have to worry about parentTextFrames being longer than 1 because the insertionPoint can only be in one frame—if the selection was something like a paragraph it might crossover multiple frames.
Copy link to clipboard
Copied
Something like this must have been what I was thinking of when I erroneously said there was no parent text frame of a cell insertion. Or I didn't have enough coffee yet. Good stuff.
Copy link to clipboard
Copied
Thanks Uwe for marking this as solution.
But in my eyes, there are two solutions/correct answers in this thread, because the two approaches differ in what you get in the end and it depends on your goals.
Thanks,
Tobias
Copy link to clipboard
Copied
Hi Tobias,
no issue to mark a second reply as the correct answer.
I'll do that for you with your reply with the full script code.
Regards,
Uwe Laubender
( Adobe Community Professional )
Copy link to clipboard
Copied
Oh – I wasn’t aware, that this is possible! Thanks!
Copy link to clipboard
Copied
Thanks, Rob – this is genious!
Actually I thought about a similar approach either, but abandoned it. (I thought about looking for the paragraph sign `\r` after the table or the first character of the paragraph after the table, because finding a table start and its contents or the character behind the table’s paragraph is comparatively easy. Alternatively we could compare your
var et = app.activeDocument.stories.everyItem().tables.everyItem().getElements();
to
app.findTextPreferences.findWhat = "<0016>";
and see, which approach is faster.)
And I’ve abandoned it, because I really wanted to know, on which page a specific row (or cell) is placed. Looking for the insertion point after the table only tells me, on which page the table ends.
Thanks,
Tobias
Copy link to clipboard
Copied
app.findTextPreferences.findWhat = "<0016>";
Hi Tobias,
this also has its charme.
Especially if you want to find nested tables. Tables that were added to text cells.
From the found text the table is only one step away:
found0016Characters[i].tables[0]
Fun facts
You can even find tables with a minimum of two body rows:
app.findTextPreferences.findWhat = "<0016><0017>";
Or with a minimum of three body rows:
app.findTextPreferences.findWhat = "<0016><0017><0017>";
Warning!
Be very careful with Find Text <0016> . Do not dare to replace a found instance with nothing or even with text.
Just tested that in the GUI. InDesign crashed instantly… The same with Find Text <0017> . That's the control character "End Of Transmission Block".
NOTE: If you look for <0017> only (without <0016> in front) you probably will find more than one instance per table. It depends on the number of body rows in the table. With two body rows, one instance of <0017> is found for the table. With three body rows, two instances of <0017> are found for the same table. Etc. etc.
It seems that the result of a Text Find with <0017> returns a "character", but it's a very strange character, a "ghost character" with only one insertion point. The position of that insertion point is the one exactly after the table character. It's always that position; regardless how many rows are in the table and so how many instances of <0017> for that particular table are found.
Regards,
Uwe Laubender
( ACP )
Copy link to clipboard
Copied
Seems like the problem with getting the character after the table is the table could have space after applied, which might force the character to the next page:
Copy link to clipboard
Copied
Hi Rob,
thanks for this test! Did also some testing and yes, the "position" of the insertion point we do exploit will move along with the text that is after the table. This is also the case with a default value for Space After and other reasons so that no character of the text following the table can be rendered in the text frame with the last row of the table.
So back to Tobias' algorithm?!
Regards,
Uwe Laubender
( Adobe Community Professional )
Copy link to clipboard
Copied
I don’t see that, in my last example the insertionPoint stays with the table even when the return after is forced to the next page via a table space after:
Also, if I make a textframe and insert a single table and no additional text I get 1 character and 2 insertion points no matter how much space after is set in the table options:
//a selected textframe with one table inserted with any amount of space after
var s=app.activeDocument.selection[0];
$.writeln(s.parentStory.tables.length) //returns 1
$.writeln(s.parentStory.characters.length) //returns 1
$.writeln(s.parentStory.insertionPoints.length) //returns 2, before and after the table
Copy link to clipboard
Copied
I think, we’re done. Both approaches work. Thanks to the both of you for your help, investigations, and testing. It’s an interesting topic, because it’s on the edge of InDesign’s scripting capabilities.
I added a uservoice for this:
(Although I was a bit unshure, where to place scripting requests: in "Adobe InDesign: SDK/Scripting Bugs and Features" or in "Adobe InDesign: Feature Requests" ...)