/* Copyright 1997 Acorn Computers Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /***************************************************/ /* File : Tables.c */ /* */ /* Purpose: Table handling functions. */ /* */ /* Author : T.Cheal, adapted by A.D.Hodgkinson */ /* */ /* History: 18-Mar-97: Datestamp on code received */ /* from T.Cheal. */ /* 12-Apr-97: Tidied up, more comments, */ /* and from here on continued */ /* development of the code */ /* content. */ /* 18-Jun-97: Moved several functions */ /* that were in the */ /* reformatter here, as this */ /* is a more appropriate */ /* location for them. */ /***************************************************/ #include <stdlib.h> #include <string.h> #include "swis.h" #include "HTMLLib.h" /* HTML library API, Which will include html2_ext.h, tags.h and struct.h */ #include "wimp.h" #include "wimplib.h" #include "event.h" #include "toolbox.h" #include "svcprint.h" #include "Global.h" #include "FromROSLib.h" #include "MiscDefs.h" #include "Utils.h" #include "Memory.h" #include "Reformat.h" #include "TokenUtils.h" #include "Toolbars.h" #include "Tables.h" /* Local definitions */ #define Tables_Hourglass_WidthingTableDelay 100 #define Tables_Hourglass_FreeingTablesDelay 25 #define CorrectRowSpan(D,p) ((D)->RowSpan > (p)->RowSpan ? (p)->RowSpan : (D)->RowSpan) #define CorrectColSpan(D,p) ((D)->ColSpan > (p)->ColSpan ? (p)->ColSpan : (D)->ColSpan) /* Statics */ static int width_table_leds = 1; /* Static function prototypes */ static void tables_align_contents(browser_data * b, HStream * streambase, table_stream * table, reformat_cell * cellarray, int Row, int Column); /*************************************************/ /* tables_count_table() */ /* */ /* Works out the number of rows and columns in a */ /* given table, and writes back into the given */ /* table_stream structure these values plus */ /* pointers to two allocated blocks of memory */ /* that can hold one integer for each row and */ /* column. */ /* */ /* Parameters: Pointer to a browser_data struct, */ /* which is the parent of the table; */ /* */ /* Pointer to a table_stream struct */ /* relevant to the table. */ /* */ /* Returns: Writes to the given table_stream */ /* struct, so this must not be in a */ /* read only area. The ColSpan and */ /* RowSpan fields are filled in with */ /* the number of columns and rows */ /* respectively, and the ColOffs and */ /* RowOffs fields are filled with */ /* the pointers to the arrays of */ /* Cols and Rows ints, respectively. */ /*************************************************/ void tables_count_table(browser_data * b, table_stream * p) { table_row * R; table_headdata * D; int I; int * RowSpill; int ColCount, RowCount; int Cols, Rows; /* Avoid compiler warning - b may be used in later revisions of this code */ b = b; #ifdef TRACE if (tl & (1u<<20)) Printf("\ntables_count_table entered: 0x%x\n",(int) P); #endif RowCount = 0; R = p->List; /* Do an initial count of rows/columns just by following the */ /* structures. This isn't sufficient in itself as rowspan or */ /* colspan parameters can make the table larger than it */ /* initally seems. */ while (R) { RowCount ++; R = R->Next; } /* The RowSpill array is used to hold the number of columns */ /* defined in other rows, when a cell 'spills' onto those */ /* other rows by having a rowspan value specified for it. */ /* */ /* When scanning rows that a cell above has spilled into, */ /* the table structures themselves will skip over that */ /* column - as a cell above that row has already defined */ /* contents here. To be able to work out how many actual */ /* columns there are, then, need to fill this array with */ /* the number of columns any such cells span. */ /* */ /* If not clear already, hopefully it'll become more so in */ /* the code later which actually deals with the array. */ RowSpill = (int *) memory_alloc_and_set(RowCount * (sizeof(int)), 0); /* If allocation fails, report the error and jump out to */ /* polling - can't possibly continue here. */ if (!RowSpill) show_error_cont(make_no_table_memory_error(1)); /* Now restart the process starting the */ /* column count as the total of all row */ /* spillage to date. */ Rows = 0; Cols = 0; R = p->List; /* Move through all rows */ while (R) { #ifdef TRACE if (tl & (1u<<20)) Printf("tables_count_table row: 0x%x\n",(int) R); #endif ColCount = RowSpill[Rows]; D = R->List; /* Move through all columns in the current row */ while (D) { #ifdef TRACE if (tl & (1u<<20)) Printf("tables_count_table headdata: 0x%x\n",(int) D); #endif switch (D->Tag) { /* Items which if present, define a column */ case TagTableData: case TagTableHead: { if (D->ColSpan) ColCount += D->ColSpan; else ColCount ++; /* If this column is meant to span several rows, */ /* fill in the RowSpill arrays for all the rows */ /* it spans (except the current one) with the */ /* number of columns this tag defines. So when */ /* we get to those rows, they'll already have */ /* some columns counted in from previous columns */ /* which had a rowspan specified. */ if (D->RowSpan > 1) { int index; for (I = 1; I < CorrectRowSpan(D,p); I++) { index = Rows + I; if (index < RowCount) /* Stupidity check */ { if (D->ColSpan) RowSpill[index] += CorrectColSpan(D,p); else RowSpill[index] ++; } } } break; } } D = D->Next; } /* Keep Cols up to date as the highest column count */ /* encountered so far, and move to the next row. */ if (Cols < ColCount) Cols = ColCount; Rows ++; R = R->Next; } /* Free the temporary rowspill array */ free(RowSpill); #ifdef TRACE if (tl & (1u<<20)) Printf("Rows, Cols: %d, %d\n", Rows, Cols); #endif /* Fill in the table_stream structure with the number of rows */ /* and columns, and pointers to two allocated blocks of Rows */ /* and Cols integers. */ /* */ /* Note that if a table cell specifies colspans or rowspans */ /* that extend into rows or columns that would otherwise not */ /* be defined anywhere, these extra rows/cols will **NOT** be */ /* included in the count here. This is why loops dealing with */ /* such values *must* be very careful not to run off the end */ /* of arrays and suchlike! */ p->ColSpan = Cols; p->RowSpan = Rows; /* May be reformatting the table, in which case there will already */ /* be arrays attached to p. */ if (p->ColOffs) HtmlFree(p->ColOffs); if (p->RowOffs) HtmlFree(p->RowOffs); /* Now allocate the array, in the context of the HStream list. */ /* When the list is eventually discarded this will automatically */ /* be discard too. */ p->ColOffs = HtmlMalloc(Cols * sizeof(int), p); if (!p->ColOffs) show_error_cont(make_no_table_memory_error(10)); p->RowOffs = HtmlMalloc(Rows * sizeof(int), p); if (!p->RowOffs) show_error_cont(make_no_table_memory_error(11)); return; } /*************************************************/ /* tables_position_table() */ /* */ /* Fills in the RowOffs and ColOffs fields for */ /* the given table and all cells in a given */ /* table, taking account of multiple row and */ /* column spanning. These are integers which, */ /* for a given cell, give the number of rows and */ /* columns from the top left of the table */ /* (starting at (0, 0)) that the cell is offset */ /* by. */ /* */ /* Parameters: Pointer to a browser_data struct, */ /* which is the parent of the table; */ /* */ /* Pointer to a table_stream struct */ /* relevant to the table. */ /*************************************************/ void tables_position_table(browser_data * b, table_stream * p) { table_row * R; table_headdata * D; int Cols, Rows, ColCount; int * ColOffs; int * RowOffs; unsigned char * RowSpill; if (!p->ColSpan || !p->RowSpan) return; /* Avoid compiler warning - b may be used in later revisions of this code */ b = b; #ifdef TRACE if (tl & (1u<<20)) Printf("tables_position_table entered: 0x%x\n",(int) P); #endif Rows = 0; RowSpill = (unsigned char *) memory_alloc_and_set(p->ColSpan * p->RowSpan, 0); /* Enough for all rows & cols */ ColCount = p->ColSpan; ColOffs = p->ColOffs; RowOffs = p->RowOffs; /* If allocation fails, report the error and jump out to */ /* polling - can't possibly continue here. */ if (!RowSpill) show_error_cont(make_no_table_memory_error(2)); R = p->List; /* Loop through all rows */ while (R && Rows < p->RowSpan) { Cols = 0; /* Look through the RowSpill array for this row, skipping */ /* any columns which have already been done - marked by a */ /* non-zero value in the array. */ while (RowSpill[Rows * ColCount + Cols]) Cols++; /* Cols now points at a column that hasn't been positioned yet. */ if (R->Width) { if (ColOffs[Cols] < R->Width) ColOffs[Cols] = R->Width; } D = R->List; /* Loop through all columns pointed to by this row */ while (D && Cols < ColCount) { int C, R, I, J; switch (D->Tag) { case TagTableData: case TagTableHead: { #ifdef TRACE if (tl & (1u<<20)) Printf("tables_position_table: In scan, D (0x%x) RowOffs = %d, ColOffs = %d\n", D, Rows, Cols); #endif D->ColOffs = Cols; D->RowOffs = Rows; /* This is where this cell is, honest */ if (D->ColSpan) C = CorrectColSpan(D, p); else C = 1; if (D->RowSpan) R = CorrectRowSpan(D, p); else R = 1; /* Loops contain a stupidity check... */ for (I = Cols; I <= Cols + C - 1; I++) { for (J = Rows; J <= Rows + R - 1; J++) { /* For all columns and rows that this cell covers, mark that these */ /* have been dealt with in the RowSpill array. */ if (I < p->ColSpan && J < p->RowSpan) RowSpill[J * ColCount + I] = 0xff; } } Cols += C; /* Ensure that Cols is advanced appropriately to get past */ /* anything marked as dealt with by the above code. */ while (RowSpill[Rows * ColCount + Cols]) Cols++; } break; } D = D->Next; } Rows ++; R = R->Next; } /* Free up the temporary array and exit */ free(RowSpill); } /*************************************************/ /* tables_init_table() */ /* */ /* For a new, uninitialised array of */ /* reformat_cell structures, fills in any */ /* initial information prior to sizing and */ /* positioning the cells. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the tables; */ /* */ /* Pointer to the table_stream */ /* struct representing the table; */ /* */ /* Pointer to the cell array. */ /*************************************************/ void tables_init_table(browser_data * b, table_stream * table, reformat_cell * cellarray) { int i, size; size = table->ColSpan * table->RowSpan; if (!size) return; memset(&cellarray[0], 0, sizeof(reformat_cell) * size); for (i = 0; i < size; i++) { cellarray[i].table = table; } return; } /*************************************************/ /* tables_width_table() */ /* */ /* Works out the widths in millipoints for all */ /* cells in a given table. This is called by the */ /* reformatter routines as they try to find the */ /* width of various page elements. To find out */ /* the width of, and finally format a particular */ /* cell, this function then itself calls the */ /* reformatter. This can of course then call */ /* back here for nested tables - i.e. this is a */ /* recursive function, though it's not */ /* immediately obvious as the callers and */ /* callees are spread over a wide area of code. */ /* */ /* Because of the way tables define column */ /* widths, for each cell three reformats take */ /* place - minimum width find, maximum width */ /* find, and final decided width. */ /* */ /* Coupled with all the juggling of those maxs */ /* and mins - taking into account anything that */ /* was specified in the HTML - this is something */ /* of a monster function and it is certainly not */ /* efficient at present. Hopefully this will */ /* improve in a future interation of the tables */ /* code... */ /* */ /* Parameters: 1 for a top level call, else 0 if */ /* being called as part of a nested */ /* tables reformat session; */ /* */ /* Pointer to a browser_data struct, */ /* which is the parent of the table; */ /* */ /* Pointer to a table_stream struct */ /* relevant to the table; */ /* */ /* The available width for the table */ /* in millipoints; */ /* */ /* Pointer to an array of points to */ /* reformat_cell structures which */ /* will hold the final table layout. */ /* */ /* Returns: The table width, in millipoints. */ /*************************************************/ int tables_width_table(int toplevel, browser_data * b, table_stream * p, int available_width, reformat_cell * cellarray) { table_row * R; table_headdata * D; int I; // S, T; // For code commented out below int * ColOffs; int cellmax = p->ColSpan * p->RowSpan; int rowcount = 0; int real_width, new_width; int min_table_width, max_table_width; int remain_min, remain_max; int * mins, * maxs; int * overmins, * overmaxs; int have_colspans = 0; int have_percents = 0; int padding; #ifdef TRACE if (tl & (1u<<20)) Printf("tables_width_table entered: 0x%x %d (%d OS) 0x%x\n", (int) P, available_width, available_width / 400, cellarray); #endif if (cellmax) padding = tables_cell_padding(b, &cellarray[0]); else return 1600; /* Deal with specific table widths */ if (TABLE_HAS_WIDTH(p)) { int width, dx; switch (TABLE_WIDTH_UNITS(p)) { default: case UNITS_PIXELS: { convert_to_points(wimpt_dx(), &dx); width = TABLE_WIDTH(p) * dx; } break; case UNITS_PERCENT: { width = (TABLE_WIDTH(p) * available_width) / 100; } break; } available_width = width; } /* Allocate two arrays of ints to hold the minimum and maximum */ /* widths of the table's cells. */ maxs = (int *) memory_alloc_and_set(sizeof(int) * cellmax, 0); if (!maxs) { show_error_ret(make_no_table_memory_error(3)); return 0; } mins = (int *) memory_alloc_and_set(sizeof(int) * cellmax, 0); if (!mins) { free(maxs); show_error_ret(make_no_table_memory_error(4)); return 0; } /* Allocate arrays to hold min and max widths of columns */ /* worked out overall. */ overmaxs = (int *) memory_alloc_and_set(sizeof(int) * p->ColSpan, 0); if (!overmaxs) { free(mins); free(maxs); show_error_ret(make_no_table_memory_error(5)); return 0; } overmins = (int *) memory_alloc_and_set(sizeof(int) * p->ColSpan, 0); if (!overmins) { free(overmaxs); free(mins); free(maxs); show_error_ret(make_no_table_memory_error(6)); return 0; } /* This can get s l o w... */ _swix(Hourglass_Start, _IN(0), Tables_Hourglass_WidthingTableDelay); /* Treat the p->ColOffs field as an array of millipoint offsets */ /* from the left of the table for each column. */ ColOffs = p->ColOffs; /* First pass - find min and max widths of the columns. */ R = p->List; /* p is a table_stream, so R now points to the first table_row */ while (R && rowcount < p->RowSpan) { #ifdef TRACE if (tl & (1u<<20)) Printf("tables_width_table: Row struct 0x%x\n",(int) R); #endif /* Flick the LEDs around a bit to show progress. Not much point */ /* trying to do %s because of nested tables. */ _swix(Hourglass_LEDs, _INR(0,1), 3, width_table_leds); width_table_leds ^= 3; /* R is a table_row, so D now points to a table_headdata */ D = R->List; while (D && D->ColOffs < p->ColSpan) { #ifdef TRACE if (tl & (1u<<20)) Printf("tables_width_table: Head struct, tag 0x%x 0x%x\n",(int) D,(int) D->Tag); #endif switch(D->Tag) { case TagTableData: case TagTableHead: { int minw, maxw; int width = 0; int cellnumber = D->RowOffs * p->ColSpan + D->ColOffs; if (cellnumber < cellmax) { #ifdef TRACE if (tl & (1u<<20)) Printf("tables_width_table: Dealing with column tag 0x%x\n",(int) D->Tag); #endif /* Will need to do another scan for colspan cells */ if (D->ColSpan > 1) have_colspans = 1; if (TD_HAS_WIDTH(D)) { /* Deal with specific cell widths */ switch (TD_WIDTH_UNITS(D)) { default: case UNITS_PIXELS: { int dx; convert_to_points(wimpt_dx(), &dx); /* Millipoints per pixel in dx */ width = TD_WIDTH(D) * dx; } break; case UNITS_PERCENT: { /* Need to deal with this after anything else, as % width specifiers */ /* mean 'n% of the space left after other cells have been widthed'. */ have_percents = 1; } break; } maxs[cellnumber] = mins[cellnumber] = width; } /* Unfortunately, still need to find the minimum size. So bang */ /* goes any possible speed up from knowing it in advance. If */ /* only web page designers could count... */ reformat_find_cell_limits(toplevel, b, (HStream *) D->List, p, cellarray, D->RowOffs, D->ColOffs, &minw, &maxw); /* Take the largest - the values found from the reformatter, */ /* or any values specified in the cell. */ if (minw > mins[cellnumber]) mins[cellnumber] = minw; if (maxw > maxs[cellnumber]) maxs[cellnumber] = maxw; /* Deal with rowspans */ if (D->RowSpan > 1) { int rsc, index; for (rsc = 1; rsc < CorrectRowSpan(D, p); rsc ++) { index = p->ColSpan * rsc + D->ColOffs; /* Sanity check */ if (index >= cellmax) break; /* Write the values into the row */ mins[index] = mins[cellnumber]; maxs[index] = maxs[cellnumber]; } } /* Closure of 'if (cellnumber < cellmax)' */ } } break; /* Closure of 'switch (D->Tag)' */ } D = D->Next; /* Closure of 'while (D && ...)' */ } /* Carry on for the other rows. */ rowcount ++; R = R->Next; /* Closure of 'while (R && ...)' */ } #ifdef TRACE if (tl & (1u<<20)) { int rowc,colc; Printf("\nTable size: mins\n"); for(rowc=0;rowc<p->RowSpan;rowc++) { Printf("%d: ",rowc); for(colc=0;colc<p->ColSpan;colc++) { Printf("%d (%d) ",mins[rowc*p->ColSpan+colc],mins[rowc*p->ColSpan+colc]/400); } Printf("\n"); } Printf("\nTable size: maxs\n"); for(rowc=0;rowc<p->RowSpan;rowc++) { Printf("%d: ",rowc); for(colc=0;colc<p->ColSpan;colc++) { Printf("%d (%d) ",maxs[rowc*p->ColSpan+colc],maxs[rowc*p->ColSpan+colc]/400); } Printf("\n"); } Printf("\n"); } #endif /* Now have the mins and maxs arrays filled with the calculated */ /* minimum and maximum size of some cells; those with no width */ /* specifier, those with a pixel size specifier, but not those */ /* with percentage specifiers yet. */ /* */ /* We now need to work out the overall greatest minimum and */ /* maximum width for each column. Any cells specifying a */ /* colspan > 1 are ignored in this, as they will have a large */ /* width (they span several cells, after all). We're only */ /* interested in single cells here. */ /* */ /* Must follow the linked list here to ensure that colspans are */ /* known about and to ensure that spurious rowspan values, put */ /* into the array for the above percentage width calculation */ /* code to work, are not thought to represent real cell widths. */ /* */ /* The next step is to use this to find what is left of the */ /* available width for percentage width specifiers. */ R = p->List; rowcount = 0; while (R && rowcount < p->RowSpan) { D = R->List; while (D && D->ColOffs < p->ColSpan) { switch(D->Tag) { case TagTableData: case TagTableHead: { if (D->ColSpan <= 1) { int cellnumber = D->RowOffs * p->ColSpan + D->ColOffs; if (cellnumber < cellmax) { if (mins[cellnumber] > overmins[D->ColOffs]) overmins[D->ColOffs] = mins[cellnumber]; if (maxs[cellnumber] > overmaxs[D->ColOffs]) overmaxs[D->ColOffs] = maxs[cellnumber]; } } } break; /* Closure of 'switch (D->Tag)' */ } D = D->Next; /* Closure of 'while (D && ...)' */ } rowcount ++; R = R->Next; /* Closure of 'while (R && ...)' */ } #ifdef TRACE if (tl & (1u<<20)) { int colc; Printf("\nTable overall 1: mins\n"); for(colc=0;colc<p->ColSpan;colc++) { Printf("%d (%d) ",overmins[colc],overmins[colc]/400); } Printf("\n\nTable overall 1: maxs\n"); for(colc=0;colc<p->ColSpan;colc++) { Printf("%d (%d) ",overmaxs[colc],overmaxs[colc]/400); } Printf("\n\n"); } #endif /* OK, now deal with percentage specifiers. First, work out the */ /* remaining widths. */ remain_min = remain_max = available_width; for (I = 0; I < p->ColSpan; I++) { remain_min -= overmins[I]; remain_max -= overmaxs[I]; } if (remain_min < 0) remain_min = 0; if (remain_max < 0) remain_max = 0; /* Now go through the table structure working out the widths from this, */ /* doing it all on a cell by cell level. */ if (have_percents) { R = p->List; rowcount = 0; while (R && rowcount < p->RowSpan) { D = R->List; while (D && D->ColOffs < p->ColSpan) { switch(D->Tag) { case TagTableData: case TagTableHead: { if (TD_HAS_WIDTH(D) && TD_WIDTH_UNITS(D) == UNITS_PERCENT) { int min_p_w; int max_p_w; int cellnumber = D->RowOffs * p->ColSpan + D->ColOffs; if (cellnumber < cellmax) { min_p_w = (remain_min * TD_WIDTH(D)) / 100; max_p_w = (remain_max * TD_WIDTH(D)) / 100; #ifdef TRACE if (tl & (1u<<20)) Printf("min_p_w, max_p_w: %d, %d\n", min_p_w, max_p_w); #endif /* Does this work out larger than values already obtained? */ if (min_p_w > mins[cellnumber]) mins[cellnumber] = min_p_w; if (max_p_w > maxs[cellnumber]) maxs[cellnumber] = max_p_w; /* Deal with rowspans */ if (D->RowSpan > 1) { int rsc, index; for (rsc = 1; rsc < CorrectRowSpan(D, p); rsc ++) { index = p->ColSpan * rsc + D->ColOffs; /* Sanity check */ if (index >= cellmax) break; /* Write the values into the row */ mins[index] = mins[cellnumber]; maxs[index] = maxs[cellnumber]; } } /* Closure of 'if (cellnumber < cellmax)' */ } } } break; /* Closure of 'switch (D->Tag)' */ } D = D->Next; /* Closure of 'while (D && ...)' */ } rowcount ++; R = R->Next; /* Closure of 'while (R && ...)' */ } /* Tedious but true - we now have to rescan the updated */ /* mins and maxs array to see if the percentage specified */ /* cells will affect the overall column sizes in overmins */ /* and overmaxs. */ R = p->List; rowcount = 0; while (R && rowcount < p->RowSpan) { D = R->List; while (D && D->ColOffs < p->ColSpan) { switch(D->Tag) { case TagTableData: case TagTableHead: { if (D->ColSpan <= 1) { int cellnumber = D->RowOffs * p->ColSpan + D->ColOffs; if (cellnumber < cellmax) { if (mins[cellnumber] > overmins[D->ColOffs]) overmins[D->ColOffs] = mins[cellnumber]; if (maxs[cellnumber] > overmaxs[D->ColOffs]) overmaxs[D->ColOffs] = maxs[cellnumber]; } } } break; /* Closure of 'switch (D->Tag)' */ } D = D->Next; /* Closure of 'while (D && ...)' */ } rowcount ++; R = R->Next; /* Closure of 'while (R && ...)' */ } #ifdef TRACE if (tl & (1u<<20)) { int colc; Printf("\nTable overall 2 (post-percents): mins\n"); for(colc=0;colc<p->ColSpan;colc++) { Printf("%d (%d) ",overmins[colc],overmins[colc]/400); } Printf("\n\nTable overall 2 (post-percents): maxs\n"); for(colc=0;colc<p->ColSpan;colc++) { Printf("%d (%d) ",overmaxs[colc],overmaxs[colc]/400); } Printf("\n\n"); } #endif /* Closure of 'if (have_percents)' */ } // ***************** DON'T FORGET! // That the old tables source // has the better distribution code base on the // RFC1942 table autowidth stuff. // ***************** /* Now deal with cells with colspans > 1. For each case, add up */ /* the width of the spanned cells from the array just calculated. */ /* If these work out more than the width calculated for that */ /* cell, then nothing needs to be done; else distribute the */ /* difference over the spanned cells. */ if (have_colspans) { R = p->List; rowcount = 0; while (R && rowcount < p->RowSpan) { D = R->List; while (D && D->ColOffs < p->ColSpan) { switch(D->Tag) { case TagTableData: case TagTableHead: { if (D->ColSpan > 1) { int currcol; int cellnumber = D->RowOffs * p->ColSpan + D->ColOffs; int spanned_total_mins = 0; int spanned_total_maxs = 0; if (cellnumber < cellmax) { /* Count the spanned columns */ for ( currcol = D->ColOffs; currcol < D->ColOffs + D->ColSpan && currcol < p->ColSpan; currcol ++ ) spanned_total_mins += overmins[currcol], spanned_total_maxs += overmaxs[currcol]; #ifdef TRACE if (tl & (1u<<20)) Printf("spanned total maxs, mins: %d, %d (%d, %d)\n", spanned_total_mins, spanned_total_maxs, spanned_total_mins/400, spanned_total_maxs/400); #endif /* If not big enough, distribute the difference over the columns */ if (spanned_total_mins < mins[cellnumber]) { int min_diff = mins[cellnumber] - spanned_total_mins; int basic = min_diff / D->ColSpan; int remainder = min_diff - (basic * D->ColSpan); /* Distribute for minimums, handling rounding carefully */ for ( currcol = D->ColOffs; currcol < D->ColOffs + D->ColSpan && currcol < p->ColSpan; currcol ++ ) { if (remainder < 0) { overmins[currcol] += basic - 1; remainder ++; } else if (remainder > 0) { overmins[currcol] += basic + 1; remainder --; } else overmins[currcol] += basic; } } /* Same for maximums */ if (spanned_total_maxs < maxs[cellnumber]) { int max_diff = maxs[cellnumber] - spanned_total_maxs; int basic = max_diff / D->ColSpan; int remainder = max_diff - (basic * D->ColSpan); for ( currcol = D->ColOffs; currcol < D->ColOffs + D->ColSpan && currcol < p->ColSpan; currcol ++ ) { if (remainder < 0) { overmaxs[currcol] += basic - 1; remainder ++; } else if (remainder > 0) { overmaxs[currcol] += basic + 1; remainder --; } else overmaxs[currcol] += basic; } } /* Closure of 'if (cellnumber < cellmax)' */ } } } break; /* Closure of 'switch (D->Tag)' */ } D = D->Next; /* Closure of 'while (D && ...)' */ } rowcount ++; R = R->Next; /* Closure of 'while (R && ...)' */ } /* Closure of 'if (have_colspans)' */ } /* RFC 1942-like autolayout algorithm - if the *minimum* widths exceed */ /* the width we're trying to fit the table into, assign the min sizes */ /* anyway and (elsewhere) allow horizontal scrolling. */ min_table_width = max_table_width = 0; for (I = 0; I < p->ColSpan; I++) min_table_width += overmins[I], max_table_width += overmaxs[I]; if (min_table_width > available_width) { /* Assign minimum widths */ #ifdef TRACE if (tl & (1u<<20)) Printf("\ntables_width_table: Forced to assign minimum widths:\n\n"); #endif for (I = 0; I < p->ColSpan; I++) { ColOffs[I] = overmins[I]; #ifdef TRACE if (tl & (1u<<20)) Printf("%d: %d OS units\n", I, overmins[I] / 400); #endif } #ifdef TRACE if (tl & (1u<<20)) Printf("\n"); #endif } /* Otherwise, do the maximum widths fit in? If so, assign the minimum */ /* widths (and in future - not implemented yet - possibly linearly */ /* expand those widths out to fill the given space). */ else if (max_table_width <= available_width) { /* Assign maximum widths */ #ifdef TRACE if (tl & (1u<<20)) Printf("\ntables_width_table: Table fits, assigning maximum widths:\n\n"); #endif for (I = 0; I < p->ColSpan; I++) { ColOffs[I] = overmaxs[I]; #ifdef TRACE if (tl & (1u<<20)) Printf("%d: %d OS units\n", I, overmaxs[I] / 400); #endif } #ifdef TRACE if (tl & (1u<<20)) Printf("\n"); #endif } /* Otherwise the maximum width of the table is greater than the available */ /* space, but the minimum width is less than it. */ /* */ /* According to RFC 1942, the following widthing algorithm is best. */ else { float d, W, D; /* Must be floats as ints will overflow, hmph. */ #ifdef TRACE if (tl & (1u<<20)) Printf("\ntables_width_table: Assigning variable column widths:\n\n"); #endif W = (float) available_width - (float) min_table_width; /* Difference between minimum table width and available width */ D = (float) max_table_width - (float) min_table_width; /* Difference between minimum and maximum table width */ if (D > 0) { for (I = 0; I < p->ColSpan; I++) { d = (float) overmaxs[I] - (float) overmins[I]; /* Difference between minimum and maximum column width */ /* The following makes columns with large differences between their minimum and maximum */ /* widths wider than columns with small differences. */ ColOffs[I] = overmins[I] + (int) (d * W / D); #ifdef TRACE if (tl & (1u<<20)) Printf("%d: %d OS units\n", I, ColOffs[I] / 400); #endif } } #ifdef TRACE else { if (tl & (1u<<20)) Printf("...can't, max_table_width = min_table_width\n"); } if (tl & (1u<<20)) Printf("\n"); #endif } /* Add up the widths */ real_width = 0; for (I = 0; I < p->ColSpan; I++) real_width += ColOffs[I]; /* Did the table specify a width to fit to? */ if (TABLE_HAS_WIDTH(p)) { /* If the width is less than that available, distribute */ /* the extra width over the table to make it fit. */ if (real_width && real_width < available_width) { float W, add = 0; #ifdef TRACE if (tl & (1u<<20)) Printf("\ntables_width_table: Resizing all columns to fit space:\n\n"); #endif W = (float) available_width - (float) real_width; for (I = 0; I < p->ColSpan; I++) { add = W * ((float) ColOffs[I] / (float) real_width); ColOffs[I] += (int) add; #ifdef TRACE if (tl & (1u<<20)) Printf("%d: %d OS units\n", I, ColOffs[I] / 400); #endif } } #ifdef TRACE else { if (tl & (1u<<20)) Printf("...can't, real_width = 0\n"); } if (tl & (1u<<20)) Printf("\n"); #endif } /* We've finished with the temporary mins and maxs arrays */ free(maxs); free(mins); free(overmaxs); free(overmins); /* Now we need to go back and reformat the cell contents to the known width */ rowcount = 0; R = p->List; while (R && rowcount < p->RowSpan) { D = R->List; while (D && D->ColOffs < p->ColSpan) { switch(D->Tag) { case TagTableData: case TagTableHead: { /* Use the tag's number of columns offset from the left */ /* of the table as an index into the array of millipoint */ /* offsets. */ new_width = abs(ColOffs[D->ColOffs]); /* If the tag spans more than one column, add in the widths */ /* of the extra columns it covers. */ if (D->ColSpan > 1) { int index; for(I = 1; I < D->ColSpan; I++) { index = D->ColOffs + I; if (index < p->ColSpan) new_width += abs(ColOffs[index]) - padding; } } /* Reformat the cell, recursing for inner tables */ real_width = tables_width_cell(toplevel, b, (HStream *) D->List, p, cellarray, new_width, D->RowOffs, D->ColOffs); // Never should be run, but low overhead to check it // and adds robustness; so keep this code? // // /* If the returned width is greater than the cell width worked */ // /* out above, distribute that extra width evenly through the */ // /* columns that the tag spans. */ // // if (new_width < real_width) // { // T = real_width - new_width; // // if (D->ColSpan) C = D->ColSpan; // else C = 1; // // T = T / C; // // for (I = 0; I < C; I++) // { // // Should keep the lowest value too if S<0 somewhere else... // // S = (ColOffs[D->ColOffs + I] >= 0) ? 1 : -1; // // ColOffs[D->ColOffs + I] += S * T; // } // } } break; } D = D->Next; } rowcount ++; R = R->Next; } /* Finally, add up all the widths of the columns and return it */ max_table_width = 0; for (I = 0; I < p->ColSpan; I++) max_table_width += abs(ColOffs[I]); #ifdef TRACE if (tl & (1u<<20)) Printf("tables_width_table: Finished, returning %d (%d OS)\n", max_table_width, max_table_width / 400); #endif _swix(Hourglass_Off, 0); return max_table_width; } /*************************************************/ /* tables_width_cell() */ /* */ /* Returns the actual width of a given table */ /* cell after attempting to reformat it to a */ /* given width. */ /* */ /* Parameters: 1 for a top level call, 0 if */ /* being called as part of a nested */ /* table parse; */ /* */ /* Pointer to a browser_data struct */ /* relevant to the table; */ /* */ /* Pointer to the first HStream */ /* structure in the stream that the */ /* cell is to contain; */ /* */ /* Pointer to the table_stream */ /* struct representing the table; */ /* */ /* Pointer to the table's array of */ /* reformat_cell structures; */ /* */ /* Allowed column width (millipts); */ /* */ /* Row number of the cell; */ /* */ /* Column number of the cell. */ /* */ /* Returns: Actual width after formatting, */ /* in millipoints. */ /*************************************************/ int tables_width_cell(int toplevel, browser_data * b, HStream * streambase, table_stream * table, reformat_cell * cellarray, int ColWidth, int Row, int Column) { return reformat_format_cell(toplevel, b, streambase, table, cellarray, ColWidth, Row, Column); } /*************************************************/ /* tables_height_table() */ /* */ /* Works out the heights in millipoints for all */ /* cells in a given table. This may be called */ /* from recursively called functions, but does */ /* not enter into recursion itself as the height */ /* information for particular cells is stored */ /* when finding the width - there's no need to */ /* call the reformatter again. */ /* */ /* Parameters: 1 for a top level call, else 0 if */ /* being called as part of a nested */ /* tables reformat session; */ /* */ /* Pointer to a browser_data struct, */ /* which is the parent of the table; */ /* */ /* Pointer to a table_stream struct */ /* relevant to the table; */ /* */ /* Pointer to an array of points to */ /* reformat_cell structures which */ /* will hold the final table layout. */ /* */ /* Returns: Height of the whole table, in */ /* millipoints. */ /*************************************************/ int tables_height_table(int toplevel, browser_data * b, table_stream * p, reformat_cell * cellarray) { int ReturnHeight = 0, Height, NewWidth; int * RowOffs; int * ColOffs; int Row; table_row * R; table_headdata * D; int I, C; int cellcount = 0; int cellmax = p->ColSpan * p->RowSpan; int padding; if (cellmax) padding = tables_cell_padding(b, &cellarray[0]) * 2; else return 1600; #ifdef TRACE if (tl & (1u<<20)) Printf("tables_height_table entered: 0x%x 0x%x\n",(int) P,(int) cellarray); #endif /* To work out height, must know the width (so that the */ /* reformatter knows what width to wrap to, etc.). So */ /* must only call this *after* tables_width_table. */ RowOffs = p->RowOffs; ColOffs = p->ColOffs; Row = 0; R = p->List; while (R && cellcount < cellmax && Row < p->RowSpan) { D = R->List; while (D && cellcount < cellmax && Row < p->RowSpan) { switch (D->Tag) { case TagTableData: case TagTableHead: { /* Work out the width that this cell uses */ if (D->ColOffs < p->ColSpan && D->RowOffs < p->RowSpan) { NewWidth = abs(ColOffs[D->ColOffs]); if (D->ColSpan > 1) { int index; for (I = 1; I < D->ColSpan; I++) { index = D->ColOffs + I; if (index < p->ColSpan) NewWidth += abs(ColOffs[index]); } } /* Find the required cell height (this is not */ /* a recursive call - the height is stored in */ /* the relevant place by tables_width_cell called from */ /* tables_width_table above, which is where the */ /* recursion is handled). */ Height = tables_height_cell(toplevel, b, (HStream *) D->List, p, cellarray, NewWidth, D->RowOffs, D->ColOffs); if (TD_HAS_HEIGHT(D) && TD_HEIGHT_UNITS(D) == UNITS_PIXELS) /* Can't see how a cell height in % could possibly be meaningful... */ { int dy, hc; convert_to_points(2, &dy); /* Authors assume square pixels, so do a 2 OS unit to 1 pixel scaling */ hc = TD_HEIGHT(D) * dy; if (hc > Height) Height = hc; } if (D->RowSpan) C = CorrectRowSpan(D, p); else C = 1; /* Distribute the height over the rows this cell spans */ Height = Height / C; for (I = 0; I < C; I++) { if (Height > RowOffs[Row + I]) RowOffs[Row + I] = Height; } } } break; } cellcount++; D = D->Next; } #ifdef TRACE if (tl & (1u<<20)) Printf("tables_height_table: Row %d, height %d (%d OS)\n", Row, RowOffs[Row], RowOffs[Row] / 400); #endif ReturnHeight += RowOffs[Row]; Row ++; R=R->Next; } #ifdef TRACE if (tl & (1u<<20)) Printf("tables_height_table: Finished, returning %d (%d OS)\n\n", ReturnHeight, ReturnHeight / 400); #endif return ReturnHeight; } /*************************************************/ /* tables_height_cell() */ /* */ /* Returns the height of a cell widthed by a */ /* previous call to tables_width_cell. */ /* */ /* Parameters: 1 for a top level call, 0 if */ /* being called as part of a nested */ /* table parse; */ /* */ /* Pointer to a browser_data struct */ /* relevant to the table; */ /* */ /* Pointer to the first HStream */ /* structure in the stream that the */ /* cell is to contain; */ /* */ /* Pointer to the table_stream */ /* struct representing the table; */ /* */ /* Pointer to the table's array of */ /* reformat_cell structures; */ /* */ /* Allowed column width (millipts - */ /* this is not used at present); */ /* */ /* Row number of the cell; */ /* */ /* Column number of the cell. */ /* */ /* Returns: Cell height in millipoints. */ /*************************************************/ int tables_height_cell(int toplevel, browser_data * b, HStream * streambase, table_stream * table, reformat_cell * cellarray, int ColWidth, int Row, int Column) { int cellindex = Row * table->ColSpan + Column; reformat_cell * c; /* Can't do anything if the cell index is out of range */ if (cellindex >= table->RowSpan * table->ColSpan) return 1600; else c = &cellarray[cellindex]; /* OK, so not used for now, but may be - this is to avoid a compiler warning */ b = b; #ifdef TRACE if (tl & (1u<<20)) { Printf("tables_height_cell: %p %p %d %d %d\n",streambase,table,ColWidth,Row,Column); Printf("tables_height_cell returning %d\n",c->height); } #endif return c->height; } /*************************************************/ /* tables_fix_table() */ /* */ /* Having found the width and height of the */ /* cells in a given table, and their row/column */ /* offset from the top left, this fixes the */ /* cell positions as x and y coordinates */ /* in millipoints from the top left. */ /* */ /* Parameters: Pointer to a browser_data struct, */ /* which is the parent of the table; */ /* */ /* Pointer to a table_stream struct */ /* relevant to the table; */ /* */ /* Pointer to an array of points to */ /* reformat_cell structures which */ /* will hold the final table layout. */ /*************************************************/ void tables_fix_table(browser_data * b, table_stream * p, reformat_cell * cellarray) { table_row * R; table_headdata * D; int I; int Width, Height; int * ColOffs; int * RowOffs; int CurX, CurY; int cellcount = 0; int cellmax = p->ColSpan * p->RowSpan; /* Have ColOffs and RowOffs as the arrays holding millipoint */ /* offsets from the top left of the table. */ R = p->List; ColOffs = p->ColOffs; RowOffs = p->RowOffs; while (R && cellcount < cellmax) { D = R->List; /* In here, D->RowOffs or D->ColOffs will be the number of */ /* rows / columns from the top left that this cell is */ /* offset by. */ while (D && cellcount < cellmax) { if (D->ColOffs < p->ColSpan && D->RowOffs < p->RowSpan) { /* If the cell is offset horizontally, add up the widths of */ /* all the columns before it into CurX. */ CurX = 0; if (D->ColOffs) { for (I = 0; I < D->ColOffs; I++) { CurX += abs(ColOffs[I]); } } CurY = 0; /* Similarly, add up the row heights */ if (D->RowOffs) { for(I = 0; I < D->RowOffs; I++) { CurY -= abs(RowOffs[I]); } } /* Set Width to the width of this cell, */ /* taking account of column spanning. */ Width = abs(ColOffs[D->ColOffs]); if (D->ColSpan > 1) { for (I = 1; I < D->ColSpan; I++) { Width += abs(ColOffs[D->ColOffs + I]); } } /* Do the same for the height */ Height = abs(RowOffs[D->RowOffs]); if (D->RowSpan > 1) { for (I = 1; I < CorrectRowSpan(D, p); I++) { Height += abs(RowOffs[D->RowOffs + I]); } } /* We now know the X and Y millipoint offsets of the cell and */ /* its width and height (again, in millipoints). Now call */ /* tables_fix_cell to fix the positions of the cell inside */ /* the relevant data structure components. */ switch(D->Tag) { case TagTableData: tables_fix_cell(b, (HStream *) D->List, p, cellarray, CurX, CurY, Width, Height, D->RowOffs, D->ColOffs); break; case TagTableHead: tables_fix_cell(b, (HStream *) D->List, p, cellarray, CurX, CurY, Width, Height, D->RowOffs, D->ColOffs); break; } } cellcount++; D = D->Next; } R = R->Next; } } /*************************************************/ /* tables_fix_cell() */ /* */ /* Fixes the position of a given table cell as */ /* an offset from the top left of the table in */ /* millipoints. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* owning the table; */ /* */ /* Pointer to the first HStream */ /* structure in the stream that the */ /* cell is to contain; */ /* */ /* Pointer to the table_stream */ /* struct representing the table; */ /* */ /* Pointer to the table's array of */ /* reformat_cell structures; */ /* */ /* X offset from top left, in */ /* millipoints; */ /* */ /* Yoffset from top left, in */ /* millipoints; */ /* */ /* Total cell width (millipoints); */ /* */ /* Total cell height (millipoints); */ /* */ /* Row number of the cell; */ /* */ /* Column number of the cell. */ /*************************************************/ void tables_fix_cell(browser_data * b, HStream * streambase, table_stream * table, reformat_cell * cellarray, int x, int y, int Width, int Height, int Row, int Column) { int cellindex = Row * table->ColSpan + Column; reformat_cell * c; #ifdef TRACE if (tl & (1u<<20)) Printf("tables_fix_cell: %p %p %d %d\n",streambase,table,Row,Column); #endif /* Can't do anything if the cell index is out of range */ if (cellindex >= table->RowSpan * table->ColSpan) return; else c = &cellarray[cellindex]; //the 3200's in here should be replaced by border sizes in due course c->x = x + Column;// * 3200 + 1600; c->y = y - Row;// * 3200 - 1600; c->cellwidth = Width; c->cellheight = Height; /* Post-correct Y for VALIGN considerations */ tables_align_contents(b, streambase, table, cellarray, Row, Column); /* Finished */ #ifdef TRACE if (tl & (1u<<20)) Printf("tables_fix_cell: Successful\n"); #endif } /*************************************************/ /* tables_align_contents() */ /* */ /* Once the cellheight field of a reformat_cell */ /* struct is filled in, it is possible to look */ /* at the VALIGN specification on the table */ /* cell tag represented by the reformat_cell */ /* struct and align the cell contents in an */ /* appropriate fashion. This is done by */ /* increasing the height of the first line as */ /* required, shifting the y coordinate of all */ /* subsequent lines as needed, and increasing */ /* the used height value in the cell to match. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* owning the table; */ /* */ /* Pointer to the first HStream */ /* structure in the stream that the */ /* cell is to contain; */ /* */ /* Pointer to the table_stream */ /* struct representing the table; */ /* */ /* Pointer to the table's array of */ /* reformat_cell structures; */ /* */ /* Row number of the cell; */ /* */ /* Column number of the cell. */ /*************************************************/ static void tables_align_contents(browser_data * b, HStream * streambase, table_stream * table, reformat_cell * cellarray, int Row, int Column) { int cellindex = Row * table->ColSpan + Column; int valign_offset_mp, valign_offset_os; table_headdata * head; reformat_cell * c; #ifdef TRACE if (tl & (1u<<20)) Printf("tables_align_contents: %p %p %d %d\n",streambase,table,Row,Column); #endif /* Can't do anything if the cell index is out of range */ if (cellindex >= table->RowSpan * table->ColSpan) return; else c = &cellarray[cellindex]; /* If the cell has no lines, again, can't do anything */ if (!c->nlines) return; /* OK, so not used for now, but may be - this is to avoid a compiler warning */ b = b; /* Work out the vertical alignment offset value in OS units and millipoints */ head = streambase ? (table_headdata *) streambase->parent : NULL; if (head) { if (TD_VALIGN(head) == ALIGN_BOTTOM) valign_offset_mp = c->cellheight - c->height; else if (TD_VALIGN(head) == ALIGN_MIDDLE) valign_offset_mp = (c->cellheight - c->height) / 2; else valign_offset_mp = 0; convert_to_os(valign_offset_mp, &valign_offset_os); /* Only proceed if there's a change to be made */ if (valign_offset_mp) { int l; /* Shift the y coordinates of the lines */ for (l = 0; l < c->nlines; l++) c->ldata[l].y -= valign_offset_os; /* Increase the used height value */ c->height += valign_offset_mp; } } #ifdef TRACE if (tl & (1u<<20)) Printf("tables_align_contents: Successful\n"); #endif } /*************************************************/ /* tables_free_memory() */ /* */ /* Goes through the lines of a reformat_cell */ /* structure, freeing all allocated memory */ /* relating to any tables in that list; that is, */ /* the lines and chunks allocated in any assoc- */ /* iated cells. Does not free the given cell's */ /* line or chunk arrays, or any HTMLLib */ /* allocated data such as the cell arrays */ /* allocated through HTMLLib or the HStream */ /* lists (HtmlStreamFree should be used for */ /* those). */ /* */ /* Parameters: 1 for top level call, 0 for a */ /* recursive one (only used for the */ /* hourglass); */ /* */ /* Pointer to a browser_data struct */ /* relevant to the tables; */ /* */ /* Pointer to a reformat_cell struct */ /* holding the line array; */ /* */ /* Line number in that line array */ /* from which freeing is to start. */ /*************************************************/ void tables_free_memory(int toplevel, browser_data * b, reformat_cell * d, int line) { int l, c; if (!d->nlines || line >= d->nlines) return; if (line < 0) line = 0; if (toplevel) _swix(Hourglass_Start, _IN(0), Tables_Hourglass_FreeingTablesDelay); /* Scan through the lines from last line to the requested one */ /* (this is so that flex thrashage will hopefully be reduced, */ /* as other cells in lines of higher numbers will on average */ /* appear in memory above cells in lines of lower numbers). */ for (l = d->nlines - 1; l >= line; l--) { /* Scan through all chunks on the line */ for (c = d->ldata[l].chunks + d->ldata[l].n - 1; c >= d->ldata[l].chunks; c--) { /* Does the chunk represent a table? */ if (d->cdata[c].t->tagno == TAG_TABLE) { table_stream * table = (table_stream *) d->cdata[c].t; table_row * row = NULL; table_headdata * head = NULL; reformat_cell * cellarray = table->cells; reformat_cell * cell; int cellindex; int cellcount = 0; int cellmax = table->ColSpan * table->RowSpan; if (cellarray) { row = table->List; while (row && cellcount < cellmax) { head = row->List; while (head && cellcount < cellmax) { switch (head->Tag) { case TagTableData: case TagTableHead: { cellindex = head->RowOffs * table->ColSpan + head->ColOffs; if (cellindex < cellmax) { cell = &cellarray[cellindex]; /* Recursive call to free any nested tables inside this cell */ tables_free_memory(0, b, cell, 0); /* Free the line and chunk lists for the cell */ if (cell->ldata) flex_free((flex_ptr) &cell->ldata); if (cell->cdata) flex_free((flex_ptr) &cell->cdata); cell->nlines = 0; } } break; /* Closure of 'switch (head->Tag)' */ } cellcount++; head = head->Next; /* Closure of 'while (head && ...)' */ } row = row->Next; /* Closure of 'while (row && ...)' */ } /* Closure of 'if (cellarray)' */ } /* Closure of 'if (d->cdata[c].t->tagno == TAG_TABLE)' */ } /* Closure of loop scanning this line's chunks */ } if (toplevel) _swix(Hourglass_Percentage, _IN(0), (100 * (d->nlines - l - 1)) / d->nlines); /* Closure of loop scanning the cell's lines */ } if (toplevel) _swix(Hourglass_Off, 0); } /*************************************************/ /* tables_cell_padding() */ /* */ /* Works out the cell padding (internal top, */ /* left, right and bottom margin) for a given */ /* table cell, in millipoints. */ /* */ /* Returns: Cell padding (internal margin), in */ /* millipoints. */ /*************************************************/ int tables_cell_padding(browser_data * b, reformat_cell * cell) { int margin = TABLES_DEFAULT_CELLPADDING; // This works, but the external stuff that calls it will start messing // up for non-zero returns, since I didn't get the time to finish the // implementation. **AGAIN**. To be continued (perhaps). return 0; if (TABLE_HAS_CELLPADDING(cell->table)) convert_to_points(TABLE_CELLPADDING(cell->table) * wimpt_dx(), &margin); return margin; }