Friday, December 30, 2005


Height of a Table (continued)

Some time has passed because I had to make progress on a different part of the project, but now I'm back to this task. So, we have a reference to our table and we know how to get the size of each gap that must be filled. But we still need to work out which rows fall on which pages.

It appears that the way to tackle this is to simple walk through all the rows of the table looking at the parent text frame of each. However, there's an immediate complicaton:
var myRows = myTable.rows;[0].cells[0]);
reveals that the header row (in this case there is just one) is row 0. This script can't afford to change the header or footer rows (if there are any) because that will affect every page and potentially could change the contents of one of the later frames (possibly even creating an overflow). The two table properties headerRowCount and footerRowCount will help us here:
var myRows = myTable.rows
var myLim = myRows.length - myTable.footerRowCount;
var myTF = myTable.rows[myTable.headerRowCount].cells[0].texts[0].parentTextFrames[0];
var firstRow = myTable.headerRowCount;
for (var j = myTable.headerRowCount; myLim > j; j++) {
  // Walk through the rows
By starting at rows[myTable.headerRowCount] we start at the first row after the headers, and by subtracting myTable.footerRowCount from the total number of rows, we avoid operating on the footers.

myTF is initialized to be a reference to the first text frame that contains the table and firstRow tells us the number of the first (non-header) row of the text frame we're about to process. So now we're ready to walk through the rows. We need to look for a change in the parent text frame at which point we can process the frame we just left before moving on. But we mustn't forget that the last frame will end without us moving on to the next, so we need an extra test to see if we're dealing with the last row.

For the moment, let's put off the actual processing of each text frame because we'll write a function to do that. Let's just get a reference to the frame that needs processing and hand off to the function enough information that it knows what to do. So, inside our loop, we need:
  myCurTF = myRows[j].cells[o].texts[0].parentTextFrames[0];
  if ((myCurTF == myTF) && (j != myLim - 1)) continue;
  (j == myLim - 1) ? k = j : k = j-1;
  processFrame(myTF, myTable, firstRow, k);
  firstRow = j;
  myTF = myCurTF;
The first thing we do is check to see if we've reached either the start of a new frame or the end of the last frame. Hey! Wait a minute. How can you use an AND construction (&&) to test for an OR condition? Well, even as I wrote that sentence I found myself wondering the same thing. What converts an AND test to an OR is the "NOT" contained in the second test. That condition is false once only, when we get to the end of the last frame. Every other time it is true, and so, the first text (are the frames the same) totally dominates this test for all but the very last row.

So, only if there is a frame change or we're reached the last frame will we get past that first test. But now we need a second test because for all but the last frame, the frame we need to process ends at j-1, while the last frame ends at j. So, we test again to see if we're at the last frame, and if so we set a new variable k to the value of j and otherwise to j-1.

Aha, there's a couple of syntax issues here. First, when using the ?: construction to make a test, be sure to end the previous line with a semi-colon. JavaScript is relatively loose about semi-colons at the ends of lines, but this is one case where you must have one. Otherwise, you'll get a confusing error about it expecting a semi-colon and you'll waste time checking that you got the syntax of the ?: line right when the error is actually on the previous line. [Guess how I know that!]

More of an issue is that we really want k to be a local variable. But where to declare it? If you put it inside the ?: you'll get a syntax error. So you need to declare it in advance. Thus, we need another statement which should really go before the loop that simply reads:
var k;
In all my examples up until now, I've declared variables to be local as I created them, but you can if you wish declare them in advance without assigning them a value.

Then we call our function (which we still have to write) passing to it the frame to be processed, the table we're working on and the row numbers of the first an last row of the part of the table that is in the frame in question.

After the function has done its thing, we need to initialize our firstRow and variables for the next frame. Notice that we ignore the possibility at this stage that we just processed the last frame. In that case, we're going to hit the loop limit on the next pass and so these values will never actually be used.

And that's it for today. I'll write the function in the morning.

Comments: Post a Comment

<< Home

This page is powered by Blogger. Isn't yours?