parameter */
int cellSpacing; /* Value of CELLSPACING parameter */
int cellPadding; /* Value of CELLPADDING parameter */
int tbw; /* Width of border around whole table */
int cbw; /* Width of border around one cell */
int hspace; /* Value of HSPACE parameter */
int separation; /* Space between columns */
int margin; /* Space between left margin and 1st col */
int availWidth; /* Part of lineWidth still available */
int maxTableWidth; /* Amount of lineWidth available to table*/
int fromAbove[HTML_MAX_COLUMNS+1]; /* Cell above extends thru this row */
int min0span[HTML_MAX_COLUMNS+1]; /* Min for colspan=0 cells */
int max0span[HTML_MAX_COLUMNS+1]; /* Max for colspan=0 cells */
int reqW[HTML_MAX_COLUMNS+1]; /* Requested width for each column */
/* colMin[A][B] is the absolute minimum width of all columns between
** A+1 and B+1. colMin[B][A] is the requested width of columns between
** A+1 and B+1. This information is used to add in the constraints imposed
** by markup where N>=2.
*/
int colMin[HTML_MAX_COLUMNS+1][HTML_MAX_COLUMNS+1];
# define ColMin(A,B) colMin[(A)-1][(B)-1]
# define ColReq(A,B) colMin[(B)-1][(A)-1]
if( pStart==0 || pStart->base.type!=Html_TABLE ){
TestPoint(0);
return pStart;
}
TRACE_PUSH(HtmlTrace_Table1);
TRACE(HtmlTrace_Table1, ("Starting TableDimensions..\n"));
pStart->table.nCol = 0;
pStart->table.nRow = 0;
z = HtmlMarkupArg(pStart, "border", 0);
if( z && *z==0 ) z = "2";
tbw = pStart->table.borderWidth = z ? atoi(z) : DFLT_BORDER;
cbw = tbw>0;
z = HtmlMarkupArg(pStart, "cellpadding", 0);
cellPadding = z ? atoi(z) : DFLT_CELLPADDING;
cellSpacing = CellSpacing(htmlPtr, pStart);
#ifdef DEBUG
/* The HtmlTrace_Table4 flag causes tables to be draw with borders
** of 2, cellPadding of 5 and cell spacing of 2. This makes the
** table clearly visible. Useful for debugging. */
if( HtmlTraceMask & HtmlTrace_Table4 ){
tbw = pStart->table.borderWidth = 2;
cbw = 1;
cellPadding = 5;
cellSpacing = 2;
pStart->base.style.bgcolor = COLOR_Background;
}
#endif
separation = cellSpacing + 2*(cellPadding + cbw);
margin = tbw + cellSpacing + cbw + cellPadding;
z = HtmlMarkupArg(pStart, "hspace", 0);
hspace = z ? atoi(z) : DFLT_HSPACE;
for(p=pStart->pNext; p && p->base.type!=Html_EndTABLE; p=pNext){
pNext = p->pNext;
switch( p->base.type ){
case Html_EndTD:
case Html_EndTH:
case Html_EndTABLE:
p->ref.pOther = pStart;
TestPoint(0);
break;
case Html_EndTR:
p->ref.pOther = pStart;
inRow = 0;
TestPoint(0);
break;
case Html_TR:
p->ref.pOther = pStart;
iRow++;
pStart->table.nRow++;
iCol = 0;
inRow = 1;
maxTableWidth = availWidth = lineWidth - 2*margin;
TestPoint(0);
break;
case Html_CAPTION:
while( p && p->base.type!=Html_EndTABLE
&& p->base.type!=Html_EndCAPTION ){
p = p->pNext;
TestPoint(0);
}
break;
case Html_TD:
case Html_TH:
if( !inRow ){
/* If the | markup is omitted, insert it. */
HtmlElement *pNew = HtmlAlloc( sizeof(HtmlRef) );
if( pNew==0 ) break;
memset(pNew, 0, sizeof(HtmlRef));
pNew->base = p->base;
pNew->base.pNext = p;
pNew->base.type = Html_TR;
pNew->base.count = 0;
p->base.pPrev->base.pNext = pNew;
p->base.pPrev = pNew;
pNext = pNew;
break;
}
do{
iCol++;
}while( iCol <= pStart->table.nCol && fromAbove[iCol] > iRow );
p->cell.pTable = pStart;
colspan = p->cell.colspan;
if( colspan==0 ){
colspan = 1;
}
if( iCol + colspan - 1 > pStart->table.nCol ){
int nCol = iCol + colspan - 1;
if( nCol > HTML_MAX_COLUMNS ){
nCol = HTML_MAX_COLUMNS;
}
for(i=pStart->table.nCol+1; i<=nCol; i++){
fromAbove[i] = 0;
pStart->table.minW[i] = 0;
pStart->table.maxW[i] = 0;
min0span[i] = 0;
max0span[i] = 0;
reqW[i] = 0;
for(j=1; jtable.nCol = nCol;
}
noWrap = HtmlMarkupArg(p, "nowrap", 0)!=0;
pNext = MinMax(htmlPtr, p, &minW, &maxW, availWidth);
p->cell.pEnd = pNext;
if( (z = HtmlMarkupArg(p, "width", 0))!=0 ){
for(i=0; isdigit(z[i]); i++){}
if( strcmp(z,"*")==0 ){
requestedW = availWidth;
}else if( z[i]==0 ){
requestedW = atoi(z);
}else if( z[i]=='%' ){
/* requestedW = (atoi(z)*availWidth + 99)/100; */
requestedW = (atoi(z)*maxTableWidth + 99)/100;
}
}else{
requestedW = 0;
}
TRACE(HtmlTrace_Table1,
("Row %d Column %d: min=%d max=%d req=%d stop at %s\n",
iRow,iCol,minW,maxW,requestedW, HtmlTokenName(p->cell.pEnd)));
if( noWrap ){
minW = maxW;
}
if( iCol + p->cell.colspan <= HTML_MAX_COLUMNS ){
int min = 0;
if( p->cell.colspan==0 ){
SETMAX( min0span[iCol], minW );
SETMAX( max0span[iCol], maxW );
min = min0span[iCol] + separation;
}else if( colspan==1 ){
SETMAX( pStart->table.minW[iCol], minW );
SETMAX( pStart->table.maxW[iCol], maxW );
SETMAX( reqW[iCol], requestedW );
min = pStart->table.minW[iCol] + separation;
}else{
int n = p->cell.colspan;
SETMAX( ColMin(iCol,iCol+n-1), minW);
SETMAX( ColReq(iCol,iCol+n-1), requestedW);
min = minW + separation;
#if 0
maxW = (maxW + (n - 1)*(1-separation))/n;
for(i=iCol; itable.maxW[i], maxW );
}
#endif
}
availWidth -= min;
}
rowspan = p->cell.rowspan;
if( rowspan==0 ){
rowspan = LARGE_NUMBER;
}
if( rowspan>1 ){
for(i=iCol; icell.colspan && icell.colspan > 1 ){
iCol += p->cell.colspan - 1;
}else if( p->cell.colspan==0 ){
iCol = HTML_MAX_COLUMNS + 1;
}
break;
}
}
#ifdef DEBUG
if( HtmlTraceMask & HtmlTrace_Table6 ){
char *zSpace = "";
TRACE_INDENT;
for(i=1; i<=pStart->table.nCol; i++){
printf("%s%d:%d..%d",zSpace,i,
pStart->table.minW[i],pStart->table.maxW[i]);
if( reqW[i]>0 ){
printf("(w=%d)",reqW[i]);
}
zSpace = " ";
}
printf("\n");
for(i=1; itable.nCol; i++){
for(j=i+1; j<=pStart->table.nCol; j++){
if( ColMin(i,j)>0 ){
TRACE_INDENT;
printf("ColMin(%d,%d) = %d\n", i, j, ColMin(i,j));
}
if( ColReq(i,j)>0 ){
TRACE_INDENT;
printf("ColReq(%d,%d) = %d\n", i, j, ColReq(i,j));
}
}
}
}
#endif
/* Compute the min and max width of each column
*/
for(i=1; i<=pStart->table.nCol; i++){
int sumMin, sumReq, sumMax;
/* Reduce the max[] field to N for columns that have "width=N" */
if( reqW[i]>0 ){
pStart->table.maxW[i] = MAX(pStart->table.minW[i],reqW[i]);
}
/* Expand the width of columns marked with "colspan=0".
*/
if( min0span[i]>0 || max0span[i]>0 ){
int n = pStart->table.nCol - i + 1;
minW = (min0span[i] + (n - 1)*(1-separation))/n;
maxW = (max0span[i] + (n - 1)*(1-separation))/n;
for(j=i; j<=pStart->table.nCol; j++){
SETMAX( pStart->table.minW[j], minW );
SETMAX( pStart->table.maxW[j], maxW );
}
}
/* Expand the minW[] of columns to accomodate "colspan=N" constraints.
** The minW[] is expanded up to the maxW[] first. Then all the maxW[]s
** are expanded in proportion to their sizes. The same thing occurs
** for reqW[]s.
*/
sumReq = reqW[i];
sumMin = pStart->table.minW[i];
sumMax = pStart->table.maxW[i];
for(j=i-1; j>=1; j--){
int cmin, creq;
sumMin += pStart->table.minW[j];
sumMax += pStart->table.maxW[j];
sumReq += reqW[i];
cmin = ColMin(j,i);
if( cmin>sumMin ){
int k;
double scale;
int *tminW = pStart->table.minW;
int *tmaxW = pStart->table.maxW;
if( sumMin0 ){
scale = (double)cmin/(double)sumMin;
for(k=j; k<=i; k++){
sumMin -= tminW[k];
tminW[k] = tmaxW[k] = tminW[k]*scale;
sumMin += tminW[k];
}
}else{
int unit = cmin/(i-j+1);
for(k=j; k<=i; k++){
tminW[k] = tmaxW[k] = unit;
sumMin += tminW[k];
}
}
}
creq = ColReq(j,i);
if( creq>sumReq ){
int k;
double scale;
int *tmaxW = pStart->table.maxW;
if( sumReq0 ){
scale = (double)creq/(double)sumReq;
for(k=j; k<=i; k++){
sumReq -= reqW[k];
reqW[k] = reqW[k]*scale;
sumReq += reqW[k];
}
}else{
int unit = creq/(i-j+1);
for(k=j; k<=i; k++){
reqW[k] = unit;
sumReq += reqW[k];
}
}
}
}
}
#ifdef DEBUG
if( HtmlTraceMask & HtmlTrace_Table6 ){
char *zSpace = "";
TRACE_INDENT;
for(i=1; i<=pStart->table.nCol; i++){
printf("%s%d:%d..%d",zSpace,i,
pStart->table.minW[i],pStart->table.maxW[i]);
if( reqW[i]>0 ){
printf("(w=%d)",reqW[i]);
}
zSpace = " ";
}
printf("\n");
}
#endif
/* Compute the min and max width of the whole table
*/
n = pStart->table.nCol;
requestedW = tbw*2 + (n+1)*cellSpacing + n*2*(cellPadding + cbw);
pStart->table.minW[0] = requestedW;
pStart->table.maxW[0] = requestedW;
for(i=1; i<=pStart->table.nCol; i++){
pStart->table.minW[0] += pStart->table.minW[i];
pStart->table.maxW[0] += pStart->table.maxW[i];
requestedW += MAX(reqW[i], pStart->table.minW[i]);
}
/* Figure out how wide to draw the table */
z = HtmlMarkupArg(pStart, "width", 0);
if( z ){
int len = strlen(z);
int totalWidth;
if( len>0 && z[len-1]=='%' ){
totalWidth = (atoi(z) * lineWidth)/100;
}else{
totalWidth = atoi(z);
}
SETMAX( requestedW, totalWidth );
}
if( lineWidth && (requestedW > lineWidth) ){
TRACE(HtmlTrace_Table5,("RequestedW reduced to lineWidth: %d -> %d\n",
requestedW, lineWidth));
requestedW = lineWidth;
}
if( requestedW > pStart->table.minW[0] ){
float scale;
int *tminW = pStart->table.minW;
int *tmaxW = pStart->table.maxW;
TRACE(HtmlTrace_Table5,
("Expanding table minW from %d to %d. (reqW=%d width=%s)\n",
tminW[0], requestedW, requestedW, z));
if( tmaxW[0] > tminW[0] ){
scale = (double)(requestedW - tminW[0]) / (double)(tmaxW[0] - tminW[0]);
for(i=1; i<=pStart->table.nCol; i++){
tminW[i] += (tmaxW[i] - tminW[i]) * scale;
SETMAX(tmaxW[i], tminW[i]);
}
}else if( tminW[0]>0 ){
scale = requestedW/(double)tminW[0];
for(i=1; i<=pStart->table.nCol; i++){
tminW[i] *= scale;
tmaxW[i] *= scale;
}
}else if( pStart->table.nCol>0 ){
int unit = (requestedW - margin)/pStart->table.nCol - separation;
if( unit<0 ) unit = 0;
for(i=1; i<=pStart->table.nCol; i++){
tminW[i] = tmaxW[i] = unit;
}
}else{
tminW[0] = tmaxW[0] = requestedW;
}
pStart->table.minW[0] = requestedW;
SETMAX( pStart->table.maxW[0], requestedW );
}
#ifdef DEBUG
if( HtmlTraceMask & HtmlTrace_Table5 ){
TRACE_INDENT;
printf("Start with %s and ", HtmlTokenName(pStart));
printf("end with %s\n", HtmlTokenName(p));
TRACE_INDENT;
printf("nCol=%d minWidth=%d maxWidth=%d\n",
pStart->table.nCol, pStart->table.minW[0], pStart->table.maxW[0]);
for(i=1; i<=pStart->table.nCol; i++){
TRACE_INDENT;
printf("Column %d minWidth=%d maxWidth=%d\n",
i, pStart->table.minW[i], pStart->table.maxW[i]);
}
}
#endif
TRACE(HtmlTrace_Table1,
("Result of TableDimensions: min=%d max=%d nCol=%d\n",
pStart->table.minW[0], pStart->table.maxW[0], pStart->table.nCol));
TRACE_POP(HtmlTrace_Table1);
return p;
}
/*
** Given a list of elements, compute the minimum and maximum width needed
** to render the list. Stop the search at the first element seen that is
** in the following set:
**
** | |
**
** Return a pointer to the element that stopped the search, or to NULL
** if we ran out of data.
**
** Sometimes the value returned for both min and max will be larger than
** the true minimum and maximum. This is rare, and only occurs if the
** element string contains figures with flow-around text.
*/
static HtmlElement *MinMax(
HtmlWidget *htmlPtr, /* The Html widget */
HtmlElement *p, /* Start the search here */
int *pMin, /* Return the minimum width here */
int *pMax, /* Return the maximum width here */
int lineWidth /* Total width available */
){
int min = 0; /* Minimum width so far */
int max = 0; /* Maximum width so far */
int indent = 0; /* Amount of indentation (minimum) */
int obstacle = 0; /* Possible obstacles in the margin */
int x1 = 0; /* Length of current line assuming maximum length */
int x2 = 0; /* Length of current line assuming minimum length */
int go = 1; /* Change to 0 to stop the loop */
HtmlElement *pNext; /* Next element in the list */
for(p=p->pNext; go && p; p = pNext){
pNext = p->pNext;
switch( p->base.type ){
case Html_Text:
x1 += p->text.w;
x2 += p->text.w;
if( p->base.style.flags & STY_Preformatted ){
SETMAX( min, x1 );
SETMAX( max, x1 );
}else{
SETMAX( min, x2 );
SETMAX( max, x1 );
}
break;
case Html_Space:
if( p->base.style.flags & STY_Preformatted ){
if( p->base.flags & HTML_NewLine ){
x1 = x2 = indent;
}else{
x1 += p->space.w * p->base.count;
x2 += p->space.w * p->base.count;
}
}else if( p->base.style.flags & STY_NoBreak ){
if( x1>indent ){ x1 += p->space.w; TestPoint(0);}
if( x2>indent ){ x2 += p->space.w; TestPoint(0);}
}else{
if( x1>indent ){ x1 += p->space.w; TestPoint(0);}
x2 = indent;
}
break;
case Html_IMG:
switch( p->image.align ){
case IMAGE_ALIGN_Left:
case IMAGE_ALIGN_Right:
obstacle += p->image.w;
x1 = obstacle + indent;
x2 = indent;
SETMAX( min, x2 );
SETMAX( min, p->image.w );
SETMAX( max, x1 );
break;
default:
x1 += p->image.w;
x2 += p->image.w;
if( p->base.style.flags & STY_Preformatted ){
SETMAX( min, x1 );
SETMAX( max, x1 );
}else{
SETMAX( min, x2 );
SETMAX( max, x1 );
}
break;
}
break;
case Html_TABLE:
/* pNext = TableDimensions(htmlPtr, p, lineWidth-indent); */
pNext = TableDimensions(htmlPtr, p, 0);
x1 = p->table.maxW[0] + indent + obstacle;
x2 = p->table.minW[0] + indent;
SETMAX( max, x1 );
SETMAX( min, x2 );
x1 = indent + obstacle;
x2 = indent;
if( pNext && pNext->base.type==Html_EndTABLE ){
pNext = pNext->pNext;
}
break;
case Html_UL:
case Html_OL:
indent += HTML_INDENT;
x1 = indent + obstacle;
x2 = indent;
break;
case Html_EndUL:
case Html_EndOL:
indent -= HTML_INDENT;
if( indent < 0 ){ indent = 0; }
x1 = indent + obstacle;
x2 = indent;
break;
case Html_BLOCKQUOTE:
indent += 2*HTML_INDENT;
x1 = indent + obstacle;
x2 = indent;
break;
case Html_EndBLOCKQUOTE:
indent -= 2*HTML_INDENT;
if( indent < 0 ){ indent = 0; }
x1 = indent + obstacle;
x2 = indent;
break;
case Html_APPLET:
case Html_INPUT:
case Html_SELECT:
case Html_EMBED:
case Html_TEXTAREA:
x1 += p->input.w + p->input.padLeft;
if( p->base.style.flags & STY_Preformatted ){
SETMAX( min, x1 );
SETMAX( max, x1 );
x2 += p->input.w + p->input.padLeft;
}else{
SETMAX( min, indent + p->input.w );
SETMAX( max, x1 );
x2 = indent;
}
break;
case Html_BR:
case Html_P:
case Html_EndP:
case Html_DIV:
case Html_EndDIV:
case Html_H1:
case Html_EndH1:
case Html_H2:
case Html_EndH2:
case Html_H3:
case Html_EndH3:
case Html_H4:
case Html_EndH4:
case Html_H5:
case Html_H6:
x1 = indent + obstacle;
x2 = indent;
break;
case Html_EndTD:
case Html_EndTH:
case Html_CAPTION:
case Html_EndTABLE:
case Html_TD:
case Html_TR:
case Html_TH:
case Html_EndTR:
go = 0;
break;
default:
break;
}
if( !go ){ break; }
}
*pMin = min;
*pMax = max;
return p;
}
/* Vertical alignments:
*/
#define VAlign_Unknown 0
#define VAlign_Top 1
#define VAlign_Bottom 2
#define VAlign_Center 3
#define VAlign_Baseline 4
/*
** Return the vertical alignment specified by the given element.
*/
static int GetVerticalAlignment(HtmlElement *p, int dflt){
char *z;
int rc;
if( p==0 ) return dflt;
z = HtmlMarkupArg(p, "valign", 0);
if( z==0 ){
rc = dflt;
TestPoint(0);
}else if( stricmp(z,"top")==0 ){
rc = VAlign_Top;
TestPoint(0);
}else if( stricmp(z,"bottom")==0 ){
rc = VAlign_Bottom;
TestPoint(0);
}else if( stricmp(z,"center")==0 ){
rc = VAlign_Center;
TestPoint(0);
}else if( stricmp(z,"baseline")==0 ){
rc = VAlign_Baseline;
TestPoint(0);
}else{
rc = dflt;
TestPoint(0);
}
return rc;
}
/* Do all layout for a single table. Return the