/* $Id: table.c,v 1.2 2001/11/09 04:59:18 a-ito Exp $ */
/*
* HTML table
*/
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#ifdef __EMX__
#include <strings.h>
#endif /* __EMX__ */
#include "fm.h"
#include "html.h"
#include "parsetagx.h"
#include "Str.h"
#include "myctype.h"
#ifdef KANJI_SYMBOLS
static char *rule[] =
{"��", "��", "��", "��", "��", "��", "��", "07", "��", "��", "��", "0B", "��", "0D", "0E", " "};
static char *ruleB[] =
{"00", "��", "��", "��", "��", "��", "��", "07", "��", "��", "��", "0B", "��", "0D", "0E", " "};
#define TN_VERTICALBAR "��"
#define HORIZONTALBAR "��"
#define RULE_WIDTH 2
#else /* not KANJI_SYMBOLS */
char alt_rule[] = {
'+', '|', '-', '+', '|', '|', '+', ' ', '-', '+', '-', ' ', '+', ' ', ' ', ' '};
#if defined(__EMX__)&&!defined(JP_CHARSET)
extern int CodePage;
static char *_rule[] =
#else
static char *rule[] =
#endif
{
"<_RULE TYPE=0>+</_RULE>",
"<_RULE TYPE=1>|</_RULE>",
"<_RULE TYPE=2>-</_RULE>",
"<_RULE TYPE=3>+</_RULE>",
"<_RULE TYPE=4>|</_RULE>",
"<_RULE TYPE=5>|</_RULE>",
"<_RULE TYPE=6>+</_RULE>",
"<_RULE TYPE=7> </_RULE>",
"<_RULE TYPE=8>-</_RULE>",
"<_RULE TYPE=9>+</_RULE>",
"<_RULE TYPE=10>-</_RULE>",
"<_RULE TYPE=11> </_RULE>",
"<_RULE TYPE=12>+</_RULE>",
"<_RULE TYPE=13> </_RULE>",
"<_RULE TYPE=14> </_RULE>",
"<_RULE TYPE=15> </_RULE>"
};
#if defined(__EMX__)&&!defined(JP_CHARSET)
static char **ruleB = _rule, **rule = _rule;
static char *rule850[] = {
"\305", "\303", "\302", "\332", "\264", "\263" , "\277", "07",
"\301", "\300", "\304", "0B", "\331", "0D", "0E", " " };
static char *ruleB850[] = {
"\316", "\314", "\313", "\311" "\271", "\272", "\273", "07",
"\312", "\310", "\315", "0B", "\274", "0D", "0E", " " };
#else /* not __EMX__ or JP_CHARSET */
static char **ruleB = rule;
#endif /* not __EMX__ or JP_CHARSET */
#define TN_VERTICALBAR "<_RULE TYPE=5>|</_RULE>"
#define RULE_WIDTH 1
#endif /* not KANJI_SYMBOLS */
#define RULE(mode) (((mode)==BORDER_THICK)?ruleB:rule)
#define TK_VERTICALBAR(mode) (RULE(mode)[5])
#define BORDERWIDTH 2
#define BORDERHEIGHT 1
#define NOBORDERWIDTH 1
#define NOBORDERHEIGHT 0
#define HTT_X 1
#define HTT_Y 2
#define HTT_ALIGN 0x30
#define HTT_LEFT 0x00
#define HTT_CENTER 0x10
#define HTT_RIGHT 0x20
#define HTT_TRSET 0x40
#define HTT_VALIGN 0x700
#define HTT_TOP 0x100
#define HTT_MIDDLE 0x200
#define HTT_BOTTOM 0x400
#define HTT_VTRSET 0x800
#ifdef NOWRAP
#define HTT_NOWRAP 4
#endif /* NOWRAP */
#define TAG_IS(s,tag,len) (strncasecmp(s,tag,len)==0&&(s[len] == '>' || IS_SPACE((int)s[len])))
#ifndef max
#define max(a,b) ((a) > (b) ? (a) : (b))
#endif /* not max */
#ifndef min
#define min(a,b) ((a) > (b) ? (b) : (a))
#endif /* not min */
#ifndef abs
#define abs(a) ((a) >= 0. ? (a) : -(a))
#endif /* not abs */
#ifdef MATRIX
#ifndef MESCHACH
#include "matrix.c"
#endif /* not MESCHACH */
#endif /* MATRIX */
#ifdef MATRIX
int correct_table_matrix(struct table *, int, int, int, double);
void set_table_matrix(struct table *, int);
#endif /* MATRIX */
#ifdef MATRIX
static double
weight(int x)
{
if (x < COLS)
return (double) x;
else
return COLS * (log((double) x / COLS) + 1.);
}
static double
weight2(int a)
{
return (double) a / COLS * 4 + 1.;
}
#define sigma_td(a) (0.5*weight2(a)) /* <td width=...> */
#define sigma_td_nw(a) (32*weight2(a)) /* <td ...> */
#define sigma_table(a) (0.25*weight2(a)) /* <table width=...> */
#define sigma_table_nw(a) (2*weight2(a)) /* <table...> */
#else /* not MATRIX */
#define LOG_MIN 1.0
static double
weight3(int x)
{
if (x < 0.1)
return 0.1;
if (x < LOG_MIN)
return (double) x;
else
return LOG_MIN * (log((double) x / LOG_MIN) + 1.);
}
#endif /* not MATRIX */
static int
bsearch_2short(short e1, short *ent1, short e2, short *ent2, int base,
char *index, int nent)
{
int n = nent;
int k = 0;
int e = e1 * base + e2;
while (n > 0) {
int nn = n / 2;
int idx = index[k + nn];
int ne = ent1[idx] * base + ent2[idx];
if (ne == e) {
k += nn;
break;
}
else if (ne < e) {
n -= nn + 1;
k += nn + 1;
}
else {
n = nn;
}
}
return k;
}
static int
bsearch_double(double e, double *ent, char *index, int nent)
{
int n = nent;
int k = 0;
while (n > 0) {
int nn = n / 2;
int idx = index[k + nn];
double ne = ent[idx];
if (ne == e) {
k += nn;
break;
}
else if (ne > e) {
n -= nn + 1;
k += nn + 1;
}
else {
n = nn;
}
}
return k;
}
static int
ceil_at_intervals(int x, int step)
{
int mo = x % step;
if (mo > 0)
x += step - mo;
else if (mo < 0)
x -= mo;
return x;
}
static int
floor_at_intervals(int x, int step)
{
int mo = x % step;
if (mo > 0)
x -= mo;
else if (mo < 0)
x += step - mo;
return x;
}
#define round(x) ((int)floor((x)+0.5))
#ifndef MATRIX
static void
dv2sv(double *dv, short *iv, int size)
{
int i, k, iw;
char *index;
double *edv;
double w = 0., x;
index = NewAtom_N(char, size);
edv = NewAtom_N(double, size);
for (i = 0; i < size; i++) {
iv[i] = ceil(dv[i]);
edv[i] = (double) iv[i] - dv[i];
}
w = 0.;
for (k = 0; k < size; k++) {
x = edv[k];
w += x;
i = bsearch_double(x, edv, index, k);
if (k > i)
bcopy(index + i, index + i + 1, k - i);
index[i] = k;
}
iw = min((int) (w + 0.5), size);
if (iw == 0)
return;
x = edv[(int) index[iw - 1]];
for (i = 0; i < size; i++) {
k = index[i];
if (i >= iw && abs(edv[k] - x) > 1e-6)
break;
iv[k]--;
}
}
#endif
static int
table_colspan(struct table *t, int row, int col)
{
int i;
for (i = col + 1; i <= t->maxcol && (t->tabattr[row][i] & HTT_X); i++);
return i - col;
}
static int
table_rowspan(struct table *t, int row, int col)
{
int i;
if (!t->tabattr[row])
return 0;
for (i = row + 1; i <= t->maxrow && t->tabattr[i] &&
(t->tabattr[i][col] & HTT_Y); i++);
return i - row;
}
static int
minimum_cellspacing(int border_mode)
{
switch (border_mode) {
case BORDER_THIN:
case BORDER_THICK:
case BORDER_NOWIN:
return RULE_WIDTH;
case BORDER_NONE:
return 1;
default:
/* not reached */
return 0;
}
}
static int
table_border_width(struct table *t)
{
switch (t->border_mode) {
case BORDER_THIN:
case BORDER_THICK:
return t->maxcol * t->cellspacing + 2 * (RULE_WIDTH + t->cellpadding);
case BORDER_NOWIN:
case BORDER_NONE:
return t->maxcol * t->cellspacing;
default:
/* not reached */
return 0;
}
}
struct table *
newTable()
{
struct table *t;
int i, j;
t = New(struct table);
t->max_rowsize = MAXROW;
t->tabdata = New_N(GeneralList **, MAXROW);
t->tabattr = New_N(table_attr *, MAXROW);
t->tabheight = NewAtom_N(short, MAXROW);
#ifdef ID_EXT
t->tabidvalue = New_N(Str *, MAXROW);
t->tridvalue = New_N(Str, MAXROW);
#endif /* ID_EXT */
for (i = 0; i < MAXROW; i++) {
t->tabdata[i] = NULL;
t->tabattr[i] = 0;
t->tabheight[i] = 0;
#ifdef ID_EXT
t->tabidvalue[i] = NULL;
t->tridvalue[i] = NULL;
#endif /* ID_EXT */
}
for (j = 0; j < MAXCOL; j++) {
t->tabwidth[j] = 0;
t->minimum_width[j] = 0;
t->fixed_width[j] = 0;
}
t->cell.maxcell = -1;
t->cell.icell = -1;
t->ntable = 0;
t->tables_size = 0;
t->tables = NULL;
#ifdef MATRIX
t->matrix = NULL;
t->vector = NULL;
#endif /* MATRIX */
#if 0
t->tabcontentssize = 0;
t->indent = 0;
t->linfo.prev_ctype = PC_ASCII;
t->linfo.prev_spaces = -1;
t->linfo.prevchar = ' ';
#endif
t->trattr = 0;
t->caption = Strnew();
t->suspended_data = NULL;
#ifdef ID_EXT
t->id = NULL;
#endif
return t;
}
static void
check_row(struct table *t, int row)
{
int i, r;
GeneralList ***tabdata;
table_attr **tabattr;
short *tabheight;
#ifdef ID_EXT
Str **tabidvalue;
Str *tridvalue;
#endif /* ID_EXT */
if (row >= t->max_rowsize) {
r = max(t->max_rowsize * 2, row + 1);
tabdata = New_N(GeneralList **, r);
tabattr = New_N(table_attr *, r);
tabheight = NewAtom_N(short, r);
#ifdef ID_EXT
tabidvalue = New_N(Str *, r);
tridvalue = New_N(Str, r);
#endif /* ID_EXT */
for (i = 0; i < t->max_rowsize; i++) {
tabdata[i] = t->tabdata[i];
tabattr[i] = t->tabattr[i];
tabheight[i] = t->tabheight[i];
#ifdef ID_EXT
tabidvalue[i] = t->tabidvalue[i];
tridvalue[i] = t->tridvalue[i];
#endif /* ID_EXT */
}
for (; i < r; i++) {
tabdata[i] = NULL;
tabattr[i] = NULL;
tabheight[i] = 0;
#ifdef ID_EXT
tabidvalue[i] = NULL;
tridvalue[i] = NULL;
#endif /* ID_EXT */
}
t->tabdata = tabdata;
t->tabattr = tabattr;
t->tabheight = tabheight;
#ifdef ID_EXT
t->tabidvalue = tabidvalue;
t->tridvalue = tridvalue;
#endif /* ID_EXT */
t->max_rowsize = r;
}
if (t->tabdata[row] == NULL) {
t->tabdata[row] = New_N(GeneralList *, MAXCOL);
t->tabattr[row] = NewAtom_N(table_attr, MAXCOL);
#ifdef ID_EXT
t->tabidvalue[row] = New_N(Str, MAXCOL);
#endif /* ID_EXT */
for (i = 0; i < MAXCOL; i++) {
t->tabdata[row][i] = NULL;
t->tabattr[row][i] = 0;
#ifdef ID_EXT
t->tabidvalue[row][i] = NULL;
#endif /* ID_EXT */
}
}
}
void
pushdata(struct table *t, int row, int col, char *data)
{
check_row(t, row);
if (t->tabdata[row][col] == NULL)
t->tabdata[row][col] = newGeneralList();
pushText(t->tabdata[row][col], data);
}
void
suspend_or_pushdata(struct table *tbl, char *line)
{
if (tbl->flag & TBL_IN_COL)
pushdata(tbl, tbl->row, tbl->col, line);
else {
if (!tbl->suspended_data)
tbl->suspended_data = newTextList();
pushText(tbl->suspended_data, line);
}
}
int visible_length_offset = 0;
int
visible_length(char *str)
{
int len = 0, max_len = 0;
int status = R_ST_NORMAL;
int prev_status = status;
Str tagbuf = Strnew();
char *t, *r2;
int amp_len;
t = str;
while (*str) {
prev_status = status;
len += next_status(*str, &status);
if (status == R_ST_TAG0) {
Strclear(tagbuf);
Strcat_char(tagbuf, *str);
}
else if (status == R_ST_TAG || status == R_ST_DQUOTE || status == R_ST_QUOTE || status == R_ST_EQL) {
Strcat_char(tagbuf, *str);
}
else if (status == R_ST_AMP) {
if (prev_status == R_ST_NORMAL) {
Strclear(tagbuf);
amp_len = 0;
}
else {
Strcat_char(tagbuf, *str);
len++;
amp_len++;
}
}
else if (status == R_ST_NORMAL && prev_status == R_ST_AMP) {
Strcat_char(tagbuf, *str);
r2 = tagbuf->ptr;
t = getescapecmd(&r2);
len += strlen(t) - 1 - amp_len;
if (*r2 != '\0') {
str -= strlen(r2);
}
}
else if (status == R_ST_NORMAL && ST_IS_REAL_TAG(prev_status)) {
;
}
else if (*str == '\t') {
len--;
do {
len++;
} while ((visible_length_offset + len) % Tabstop != 0);
}
else if (*str == '\n' || *str == '\r') {
if (len > max_len) max_len = len;
len = 0;
}
else if (*str == '\n' || *str == '\r')
len = 0;
str++;
}
if (status == R_ST_AMP) {
r2 = tagbuf->ptr;
t = getescapecmd(&r2);
len += strlen(t) - 1 - amp_len;
if (*r2 != '\0') {
len += strlen(r2);
}
}
return len > max_len ? len : max_len;
}
int
maximum_visible_length(char *str)
{
int maxlen, len;
char *p;
for (p = str; *p && *p != '\t'; p++);
visible_length_offset = 0;
maxlen = visible_length(str);
if (*p == '\0')
return maxlen;
for (visible_length_offset = 1; visible_length_offset < Tabstop;
visible_length_offset++) {
len = visible_length(str);
if (maxlen < len) {
maxlen = len;
break;
}
}
return maxlen;
}
void
align(TextLine *lbuf, int width, int mode)
{
int i, l, l1, l2;
Str buf, line = lbuf->line;
if (line->length == 0) {
for (i = 0; i < width; i++)
Strcat_char(line, ' ');
lbuf->pos = width;
return;
}
buf = Strnew();
l = width - lbuf->pos;
switch (mode) {
case ALIGN_CENTER:
l1 = l / 2;
l2 = l - l1;
for (i = 0; i < l1; i++)
Strcat_char(buf, ' ');
Strcat(buf, line);
for (i = 0; i < l2; i++)
Strcat_char(buf, ' ');
break;
case ALIGN_LEFT:
Strcat(buf, line);
for (i = 0; i < l; i++)
Strcat_char(buf, ' ');
break;
case ALIGN_RIGHT:
for (i = 0; i < l; i++)
Strcat_char(buf, ' ');
Strcat(buf, line);
break;
default:
return;
}
lbuf->line = buf;
if (lbuf->pos < width)
lbuf->pos = width;
}
void
print_item(struct table *t,
int row, int col, int width,
Str buf)
{
int alignment;
TextLine *lbuf;
if (t->tabdata[row])
lbuf = popTextLine(t->tabdata[row][col]);
else
lbuf = NULL;
if (lbuf != NULL) {
check_row(t, row);
alignment = ALIGN_CENTER;
if ((t->tabattr[row][col] & HTT_ALIGN) == HTT_LEFT)
alignment = ALIGN_LEFT;
else if ((t->tabattr[row][col] & HTT_ALIGN) == HTT_RIGHT)
alignment = ALIGN_RIGHT;
else if ((t->tabattr[row][col] & HTT_ALIGN) == HTT_CENTER)
alignment = ALIGN_CENTER;
align(lbuf, width, alignment);
Strcat(buf, lbuf->line);
}
else {
lbuf = newTextLine(NULL, 0);
align(lbuf, width, ALIGN_CENTER);
Strcat(buf, lbuf->line);
}
}
#define T_TOP 0
#define T_MIDDLE 1
#define T_BOTTOM 2
void
print_sep(struct table *t,
int row, int type, int maxcol,
Str buf)
{
int forbid;
char **rulep;
int i, j, k, l, m;
#if defined(__EMX__)&&!defined(JP_CHARSET)
if(CodePage==850){
ruleB = ruleB850;
rule = rule850;
}
#endif
if (row >= 0)
check_row(t, row);
check_row(t, row + 1);
if ((type == T_TOP || type == T_BOTTOM) && t->border_mode == BORDER_THICK) {
rulep = ruleB;
}
else {
rulep = rule;
}
forbid = 1;
if (type == T_TOP)
forbid |= 2;
else if (type == T_BOTTOM)
forbid |= 8;
else if (t->tabattr[row + 1][0] & HTT_Y) {
forbid |= 4;
}
if (t->border_mode != BORDER_NOWIN)
Strcat_charp(buf, RULE(t->border_mode)[forbid]);
for (i = 0; i <= maxcol; i++) {
forbid = 10;
if (type != T_BOTTOM && (t->tabattr[row + 1][i] & HTT_Y)) {
if (t->tabattr[row + 1][i] & HTT_X) {
goto do_last_sep;
}
else {
for (k = row; k >= 0 && t->tabattr[k] && (t->tabattr[k][i] & HTT_Y); k--);
m = t->tabwidth[i] + 2 * t->cellpadding;
for (l = i + 1; l <= t->maxcol && (t->tabattr[row][l] & HTT_X); l++)
m += t->tabwidth[l] + t->cellspacing;
print_item(t, k, i, m, buf);
}
}
else {
for (j = 0; j < t->tabwidth[i] + 2 * t->cellpadding; j += RULE_WIDTH) {
Strcat_charp(buf, rulep[forbid]);
}
}
do_last_sep:
if (i < maxcol) {
forbid = 0;
if (type == T_TOP)
forbid |= 2;
else if (t->tabattr[row][i + 1] & HTT_X) {
forbid |= 2;
}
if (type == T_BOTTOM)
forbid |= 8;
else {
if (t->tabattr[row + 1][i + 1] & HTT_X) {
forbid |= 8;
}
if (t->tabattr[row + 1][i + 1] & HTT_Y) {
forbid |= 4;
}
if (t->tabattr[row + 1][i] & HTT_Y) {
forbid |= 1;
}
}
if (forbid != 15) /* forbid==15 means 'no rule at all' */
Strcat_charp(buf, rulep[forbid]);
}
}
forbid = 4;
if (type == T_TOP)
forbid |= 2;
if (type == T_BOTTOM)
forbid |= 8;
if (t->tabattr[row + 1][maxcol] & HTT_Y) {
forbid |= 1;
}
if (t->border_mode != BORDER_NOWIN)
Strcat_charp(buf, RULE(t->border_mode)[forbid]);
}
static int
get_spec_cell_width(struct table *tbl, int row, int col)
{
int i, w;
w = tbl->tabwidth[col];
for (i = col + 1; i <= tbl->maxcol; i++) {
check_row(tbl, row);
if (tbl->tabattr[row][i] & HTT_X)
w += tbl->tabwidth[i] + tbl->cellspacing;
else
break;
}
return w;
}
void
do_refill(struct table *tbl, int row, int col, int maxlimit)
{
TextList *orgdata;
TextListItem *l;
struct readbuffer obuf;
struct html_feed_environ h_env;
struct environment envs[MAX_ENV_LEVEL];
int colspan, icell;
if (tbl->tabdata[row] == NULL ||
tbl->tabdata[row][col] == NULL)
return;
orgdata = (TextList *)tbl->tabdata[row][col];
tbl->tabdata[row][col] = newGeneralList();
init_henv(&h_env, &obuf, envs, MAX_ENV_LEVEL,
(TextLineList *)tbl->tabdata[row][col],
get_spec_cell_width(tbl, row, col), 0);
if (h_env.limit > maxlimit)
h_env.limit = maxlimit;
if (tbl->border_mode != BORDER_NONE && tbl->vcellpadding > 0)
do_blankline(&h_env, &obuf, 0, 0, h_env.limit);
for (l = orgdata->first; l != NULL; l = l->next) {
if (TAG_IS(l->ptr, "<table_alt", 10)) {
int id = -1;
char *p = l->ptr;
struct parsed_tag *tag;
if ((tag = parse_tag(&p, TRUE)) != NULL)
parsedtag_get_value(tag, ATTR_TID, &id);
if (id >= 0 && id < tbl->ntable) {
int alignment;
TextLineListItem *ti;
struct table *t = tbl->tables[id].ptr;
int limit = tbl->tables[id].indent + t->total_width;
tbl->tables[id].ptr = NULL;
save_fonteffect(&h_env, h_env.obuf);
flushline(&h_env, &obuf, 0, 0, h_env.limit);
if (t->vspace > 0 && !(obuf.flag & RB_IGNORE_P))
do_blankline(&h_env, &obuf, 0, 0, h_env.limit);
if (RB_GET_ALIGN(h_env.obuf) == RB_CENTER)
alignment = ALIGN_CENTER;
else if (RB_GET_ALIGN(h_env.obuf) == RB_RIGHT)
alignment = ALIGN_RIGHT;
else
alignment = ALIGN_LEFT;
if (alignment != ALIGN_LEFT) {
for (ti = tbl->tables[id].buf->first;
ti != NULL;
ti = ti->next)
align(ti->ptr, h_env.limit, alignment);
}
appendTextLineList(h_env.buf, tbl->tables[id].buf);
if (h_env.maxlimit < limit)
h_env.maxlimit = limit;
restore_fonteffect(&h_env, h_env.obuf);
obuf.flag &= ~RB_IGNORE_P;
h_env.blank_lines = 0;
if (t->vspace > 0) {
do_blankline(&h_env, &obuf, 0, 0, h_env.limit);
obuf.flag |= RB_IGNORE_P;
}
}
}
else
HTMLlineproc1(l->ptr, &h_env);
}
completeHTMLstream(&h_env, &obuf);
flushline(&h_env, &obuf, 0, 2, h_env.limit);
if (tbl->border_mode == BORDER_NONE) {
int rowspan = table_rowspan(tbl, row, col);
if (row + rowspan <= tbl->maxrow) {
if (tbl->vcellpadding > 0 && !(obuf.flag & RB_IGNORE_P))
do_blankline(&h_env, &obuf, 0, 0, h_env.limit);
}
else {
if (tbl->vspace > 0)
purgeline(&h_env);
}
}
else {
if (tbl->vcellpadding > 0) {
if (!(obuf.flag & RB_IGNORE_P))
do_blankline(&h_env, &obuf, 0, 0, h_env.limit);
}
else
purgeline(&h_env);
}
if ((colspan = table_colspan(tbl, row, col)) > 1) {
struct table_cell *cell = &tbl->cell;
int k;
k = bsearch_2short(colspan, cell->colspan, col, cell->col, MAXCOL,
cell->index, cell->maxcell + 1);
icell = cell->index[k];
if (cell->minimum_width[icell] < h_env.maxlimit)
cell->minimum_width[icell] = h_env.maxlimit;
}
else {
if (tbl->minimum_width[col] < h_env.maxlimit)
tbl->minimum_width[col] = h_env.maxlimit;
}
}
static int
table_rule_width(struct table *t)
{
if (t->border_mode == BORDER_NONE)
return 1;
return RULE_WIDTH;
}
static void
check_cell_width(short *tabwidth, short *cellwidth,
short *col, short *colspan, short maxcell,
char *index, int space, int dir)
{
int i, j, k, bcol, ecol;
int swidth, width;
for (k = 0; k <= maxcell; k++) {
j = index[k];
if (cellwidth[j] <= 0)
continue;
bcol = col[j];
ecol = bcol + colspan[j];
swidth = 0;
for (i = bcol; i < ecol; i++)
swidth += tabwidth[i];
width = cellwidth[j] - (colspan[j] - 1) * space;
if (width > swidth) {
int w = (width - swidth) / colspan[j];
int r = (width - swidth) % colspan[j];
for (i = bcol; i < ecol; i++)
tabwidth[i] += w;
/* dir {0: horizontal, 1: vertical} */
if (dir == 1 && r > 0)
r = colspan[j];
for (i = 1; i <= r; i++)
tabwidth[ecol - i]++;
}
}
}
void
check_minimum_width(struct table *t, short *tabwidth)
{
int i;
struct table_cell *cell = &t->cell;
for (i = 0; i <= t->maxcol; i++) {
if (tabwidth[i] < t->minimum_width[i])
tabwidth[i] = t->minimum_width[i];
}
check_cell_width(tabwidth, cell->minimum_width, cell->col, cell->colspan,
cell->maxcell, cell->index, t->cellspacing, 0);
}
void
check_maximum_width(struct table *t)
{
struct table_cell *cell = &t->cell;
#ifdef MATRIX
int i, j, bcol, ecol;
int swidth, width;
cell->necell = 0;
for (j = 0; j <= cell->maxcell; j++) {
bcol = cell->col[j];
ecol = bcol + cell->colspan[j];
swidth = 0;
for (i = bcol; i < ecol; i++)
swidth += t->tabwidth[i];
width = cell->width[j] - (cell->colspan[j] - 1) * t->cellspacing;
if (width > swidth) {
cell->eindex[cell->necell] = j;
cell->necell++;
}
}
#else /* not MATRIX */
check_cell_width(t->tabwidth, cell->width, cell->col, cell->colspan,
cell->maxcell, cell->index, t->cellspacing, 0);
check_minimum_width(t, t->tabwidth);
#endif /* not MATRIX */
}
#ifdef MATRIX
static void
set_integered_width(struct table *t, double *dwidth, short *iwidth)
{
int i, j, k, n, bcol, ecol, step;
char *index, *fixed;
double *mod;
double sum = 0., x;
struct table_cell *cell = &t->cell;
int rulewidth = table_rule_width(t);
index = NewAtom_N(char, t->maxcol + 1);
mod = NewAtom_N(double, t->maxcol + 1);
for (i = 0; i <= t->maxcol; i++) {
iwidth[i] = ceil_at_intervals(ceil(dwidth[i]), rulewidth);
mod[i] = (double) iwidth[i] - dwidth[i];
}
sum = 0.;
for (k = 0; k <= t->maxcol; k++) {
x = mod[k];
sum += x;
i = bsearch_double(x, mod, index, k);
if (k > i)
bcopy(index + i, index + i + 1, k - i);
index[i] = k;
}
fixed = NewAtom_N(char, t->maxcol + 1);
bzero(fixed, t->maxcol + 1);
for (step = 0; step < 2; step++) {
for (i = 0; i <= t->maxcol; i += n) {
int nn;
char *idx;
double nsum;
if (sum < 0.5)
return;
for (n = 0; i + n <= t->maxcol; n++) {
int ii = index[i + n];
if (n == 0)
x = mod[ii];
else if (fabs(mod[ii] - x) > 1e-6)
break;
}
for (k = 0; k < n; k++) {
int ii = index[i + k];
if (fixed[ii] < 2 &&
iwidth[ii] - rulewidth < t->minimum_width[ii])
fixed[ii] = 2;
if (fixed[ii] < 1 &&
iwidth[ii] - rulewidth < t->tabwidth[ii] &&
(double) rulewidth - mod[ii] > 0.5)
fixed[ii] = 1;
}
idx = NewAtom_N(char, n);
for (k = 0; k < cell->maxcell; k++) {
int kk, w, width, m;
j = cell->index[k];
bcol = cell->col[j];
ecol = bcol + cell->colspan[j];
m = 0;
for (kk = 0; kk < n; kk++) {
int ii = index[i + kk];
if (ii >= bcol && ii < ecol) {
idx[m] = ii;
m++;
}
}
if (m == 0)
continue;
width = (cell->colspan[j] - 1) * t->cellspacing;
for (kk = bcol; kk < ecol; kk++)
width += iwidth[kk];
w = 0;
for (kk = 0; kk < m; kk++) {
if (fixed[(int) idx[kk]] < 2)
w += rulewidth;
}
if (width - w < cell->minimum_width[j]) {
for (kk = 0; kk < m; kk++) {
if (fixed[(int) idx[kk]] < 2)
fixed[(int) idx[kk]] = 2;
}
}
w = 0;
for (kk = 0; kk < m; kk++) {
if (fixed[(int) idx[kk]] < 1 &&
(double) rulewidth - mod[(int) idx[kk]] > 0.5)
w += rulewidth;
}
if (width - w < cell->width[j]) {
for (kk = 0; kk < m; kk++) {
if (fixed[(int) idx[kk]] < 1 &&
(double) rulewidth - mod[(int) idx[kk]] > 0.5)
fixed[(int) idx[kk]] = 1;
}
}
}
nn = 0;
for (k = 0; k < n; k++) {
int ii = index[i + k];
if (fixed[ii] <= step)
nn++;
}
nsum = sum - (double) (nn * rulewidth);
if (nsum < 0. && fabs(sum) <= fabs(nsum))
return;
for (k = 0; k < n; k++) {
int ii = index[i + k];
if (fixed[ii] <= step) {
iwidth[ii] -= rulewidth;
fixed[ii] = 3;
}
}
sum = nsum;
}
}
}
static double
correlation_coefficient(double sxx, double syy, double sxy)
{
double coe, tmp;
tmp = sxx * syy;
if (tmp < Tiny)
tmp = Tiny;
coe = sxy / sqrt(tmp);
if (coe > 1.)
return 1.;
if (coe < -1.)
return -1.;
return coe;
}
static double
correlation_coefficient2(double sxx, double syy, double sxy)
{
double coe, tmp;
tmp = (syy + sxx - 2*sxy) * sxx;
if (tmp < Tiny)
tmp = Tiny;
coe = (sxx - sxy) / sqrt(tmp);
if (coe > 1.)
return 1.;
if (coe < -1.)
return -1.;
return coe;
}
static double
recalc_width(double old, double swidth, int cwidth,
double sxx, double syy, double sxy,
int is_inclusive)
{
double delta = swidth - (double) cwidth;
double rat = sxy / sxx,
coe = correlation_coefficient(sxx, syy, sxy),
w, ww;
if (old < 0.)
old = 0.;
if (fabs(coe) < 1e-5)
return old;
w = rat * old;
ww = delta;
if (w > 0.) {
double wmin = 5e-3 * sqrt(syy * (1. - coe * coe));
if (swidth < 0.2 && cwidth > 0 && is_inclusive) {
double coe1 = correlation_coefficient2(sxx, syy, sxy);
if (coe > 0.9 || coe1 > 0.9)
return 0.;
}
if (wmin > 0.05)
wmin = 0.05;
if (ww < 0.)
ww = 0.;
ww += wmin;
}
else {
double wmin = 5e-3 * sqrt(syy) * fabs(coe);
if (rat > -0.001)
return old;
if (wmin > 0.01)
wmin = 0.01;
if (ww > 0.)
ww = 0.;
ww -= wmin;
}
if (w > ww)
return ww / rat;
return old;
}
static int
check_compressible_cell(struct table *t, MAT * minv,
double *newwidth, double *swidth, short *cwidth,
double totalwidth, double *Sxx,
int icol, int icell, double sxx, int corr)
{
struct table_cell *cell = &t->cell;
int i, j, k, m, bcol, ecol, span;
double delta, owidth;
double dmax, dmin, sxy;
int rulewidth = table_rule_width(t);
if (sxx < 10.)
return corr;
if (icol >= 0) {
owidth = newwidth[icol];
delta = newwidth[icol] - (double) t->tabwidth[icol];
bcol = icol;
ecol = bcol + 1;
}
else if (icell >= 0) {
owidth = swidth[icell];
delta = swidth[icell] - (double) cwidth[icell];
bcol = cell->col[icell];
ecol = bcol + cell->colspan[icell];
}
else {
owidth = totalwidth;
delta = totalwidth;
bcol = 0;
ecol = t->maxcol + 1;
}
dmin = delta;
dmax = -1.;
for (k = 0; k <= cell->maxcell; k++) {
int bcol1, ecol1;
int is_inclusive = 0;
if (dmin <= 0.)
goto _end;
j = cell->index[k];
if (j == icell)
continue;
bcol1 = cell->col[j];
ecol1 = bcol1 + cell->colspan[j];
sxy = 0.;
for (m = bcol1; m < ecol1; m++) {
for (i = bcol; i < ecol; i++)
sxy += m_entry(minv, i, m);
}
if (bcol1 >= bcol && ecol1 <= ecol) {
is_inclusive = 1;
}
if (sxy > 0.)
dmin = recalc_width(dmin, swidth[j], cwidth[j],
sxx, Sxx[j], sxy,
is_inclusive);
else
dmax = recalc_width(dmax, swidth[j], cwidth[j],
sxx, Sxx[j], sxy,
is_inclusive);
}
for (m = 0; m <= t->maxcol; m++) {
int is_inclusive = 0;
if (dmin <= 0.)
goto _end;
if (m == icol)
continue;
sxy = 0.;
for (i = bcol; i < ecol; i++)
sxy += m_entry(minv, i, m);
if (m >= bcol && m < ecol) {
is_inclusive = 1;
}
if (sxy > 0.)
dmin = recalc_width(dmin, newwidth[m], t->tabwidth[m],
sxx, m_entry(minv, m, m), sxy,
is_inclusive);
else
dmax = recalc_width(dmax, newwidth[m], t->tabwidth[m],
sxx, m_entry(minv, m, m), sxy,
is_inclusive);
}
_end:
if (dmax > 0. && dmin > dmax)
dmin = dmax;
span = ecol - bcol;
if ((span == t->maxcol + 1 && dmin >= 0.) ||
(span != t->maxcol + 1 && dmin > rulewidth * 0.5)) {
int nwidth = ceil_at_intervals(round(owidth - dmin), rulewidth);
correct_table_matrix(t, bcol, ecol - bcol, nwidth, 1.);
corr++;
}
return corr;
}
#define MAX_ITERATION 10
int
check_table_width(struct table *t, double *newwidth, MAT * minv, int itr)
{
int i, j, k, m, bcol, ecol;
int corr = 0;
struct table_cell *cell = &t->cell;
#ifdef __GNUC__
short orgwidth[t->maxcol + 1], corwidth[t->maxcol + 1];
short cwidth[cell->maxcell + 1];
double swidth[cell->maxcell + 1];
#else /* __GNUC__ */
short orgwidth[MAXCOL], corwidth[MAXCOL];
short cwidth[MAXCELL];
double swidth[MAXCELL];
#endif /* __GNUC__ */
double twidth, sxy, *Sxx, stotal;
twidth = 0.;
stotal = 0.;
for (i = 0; i <= t->maxcol; i++) {
twidth += newwidth[i];
stotal += m_entry(minv, i, i);
for (m = 0; m < i; m++) {
stotal += 2 * m_entry(minv, i, m);
}
}
Sxx = NewAtom_N(double, cell->maxcell + 1);
for (k = 0; k <= cell->maxcell; k++) {
j = cell->index[k];
bcol = cell->col[j];
ecol = bcol + cell->colspan[j];
swidth[j] = 0.;
for (i = bcol; i < ecol; i++)
swidth[j] += newwidth[i];
cwidth[j] = cell->width[j] - (cell->colspan[j] - 1) * t->cellspacing;
Sxx[j] = 0.;
for (i = bcol; i < ecol; i++) {
Sxx[j] += m_entry(minv, i, i);
for (m = bcol; m <= ecol; m++) {
if (m < i)
Sxx[j] += 2 * m_entry(minv, i, m);
}
}
}
/* compress table */
corr = check_compressible_cell(t, minv, newwidth, swidth,
cwidth, twidth, Sxx,
-1, -1, stotal, corr);
if (itr < MAX_ITERATION && corr > 0)
return corr;
/* compress multicolumn cell */
for (k = cell->maxcell; k >= 0; k--) {
j = cell->index[k];
corr = check_compressible_cell(t, minv, newwidth, swidth,
cwidth, twidth, Sxx,
-1, j, Sxx[j], corr);
if (itr < MAX_ITERATION && corr > 0)
return corr;
}
/* compress single column cell */
for (i = 0; i <= t->maxcol; i++) {
corr = check_compressible_cell(t, minv, newwidth, swidth,
cwidth, twidth, Sxx,
i, -1, m_entry(minv, i, i), corr);
if (itr < MAX_ITERATION && corr > 0)
return corr;
}
for (i = 0; i <= t->maxcol; i++)
corwidth[i] = orgwidth[i] = round(newwidth[i]);
check_minimum_width(t, corwidth);
for (i = 0; i <= t->maxcol; i++) {
double sx = sqrt(m_entry(minv, i, i));
if (sx < 0.1)
continue;
if (orgwidth[i] < t->minimum_width[i] &&
corwidth[i] == t->minimum_width[i]) {
double w = (sx > 0.5) ? 0.5 : sx * 0.2;
sxy = 0.;
for (m = 0; m <= t->maxcol; m++) {
if (m == i)
continue;
sxy += m_entry(minv, i, m);
}
if (sxy <= 0.) {
correct_table_matrix(t, i, 1, t->minimum_width[i], w);
corr++;
}
}
}
for (k = 0; k <= cell->maxcell; k++) {
int nwidth = 0, mwidth;
double sx;
j = cell->index[k];
sx = sqrt(Sxx[j]);
if (sx < 0.1)
continue;
bcol = cell->col[j];
ecol = bcol + cell->colspan[j];
for (i = bcol; i < ecol; i++)
nwidth += corwidth[i];
mwidth = cell->minimum_width[j] - (cell->colspan[j] - 1) * t->cellspacing;
if (mwidth > swidth[j] && mwidth == nwidth) {
double w = (sx > 0.5) ? 0.5 : sx * 0.2;
sxy = 0.;
for (i = bcol; i < ecol; i++) {
for (m = 0; m <= t->maxcol; m++) {
if (m >= bcol && m < ecol)
continue;
sxy += m_entry(minv, i, m);
}
}
if (sxy <= 0.) {
correct_table_matrix(t, bcol, cell->colspan[j], mwidth, w);
corr++;
}
}
}
if (itr >= MAX_ITERATION)
return 0;
else
return corr;
}
#else /* not MATRIX */
void
set_table_width(struct table *t, short *newwidth, int maxwidth)
{
int i, j, k, bcol, ecol;
struct table_cell *cell = &t->cell;
char *fixed;
int swidth, fwidth, width, nvar;
double s;
double *dwidth;
int try_again;
fixed = NewAtom_N(char, t->maxcol + 1);
bzero(fixed, t->maxcol + 1);
dwidth = NewAtom_N(double, t->maxcol + 1);
for (i = 0; i <= t->maxcol; i++) {
dwidth[i] = 0.0;
if (t->fixed_width[i] < 0) {
t->fixed_width[i] = -t->fixed_width[i] * maxwidth / 100;
}
if (t->fixed_width[i] > 0) {
newwidth[i] = t->fixed_width[i];
fixed[i] = 1;
}
else
newwidth[i] = 0;
if (newwidth[i] < t->minimum_width[i])
newwidth[i] = t->minimum_width[i];
}
for (k = 0; k <= cell->maxcell; k++) {
j = cell->index[k];
bcol = cell->col[j];
ecol = bcol + cell->colspan[j];
if (cell->fixed_width[j] < 0)
cell->fixed_width[j] = -cell->fixed_width[j] * maxwidth / 100;
swidth = 0;
fwidth = 0;
nvar = 0;
for (i = bcol; i < ecol; i++) {
if (fixed[i]) {
fwidth += newwidth[i];
}
else {
swidth += newwidth[i];
nvar++;
}
}
width = max(cell->fixed_width[j], cell->minimum_width[j])
- (cell->colspan[j] - 1) * t->cellspacing;
if (nvar > 0 && width > fwidth + swidth) {
s = 0.;
for (i = bcol; i < ecol; i++) {
if (!fixed[i])
s += weight3(t->tabwidth[i]);
}
for (i = bcol; i < ecol; i++) {
if (!fixed[i])
dwidth[i] = (width - fwidth) * weight3(t->tabwidth[i]) / s;
else
dwidth[i] = (double) newwidth[i];
}
dv2sv(dwidth, newwidth, cell->colspan[j]);
if (cell->fixed_width[j] > 0) {
for (i = bcol; i < ecol; i++)
fixed[i] = 1;
}
}
}
do {
nvar = 0;
swidth = 0;
fwidth = 0;
for (i = 0; i <= t->maxcol; i++) {
if (fixed[i]) {
fwidth += newwidth[i];
}
else {
swidth += newwidth[i];
nvar++;
}
}
width = maxwidth - t->maxcol * t->cellspacing;
if (nvar == 0 || width <= fwidth + swidth)
break;
s = 0.;
for (i = 0; i <= t->maxcol; i++) {
if (!fixed[i])
s += weight3(t->tabwidth[i]);
}
for (i = 0; i <= t->maxcol; i++) {
if (!fixed[i])
dwidth[i] = (width - fwidth) * weight3(t->tabwidth[i]) / s;
else
dwidth[i] = (double) newwidth[i];
}
dv2sv(dwidth, newwidth, t->maxcol + 1);
try_again = 0;
for (i = 0; i <= t->maxcol; i++) {
if (!fixed[i]) {
if (newwidth[i] > t->tabwidth[i]) {
newwidth[i] = t->tabwidth[i];
fixed[i] = 1;
try_again = 1;
}
else if (newwidth[i] < t->minimum_width[i]) {
newwidth[i] = t->minimum_width[i];
fixed[i] = 1;
try_again = 1;
}
}
}
} while (try_again);
}
#endif /* not MATRIX */
void
check_table_height(struct table *t)
{
int i, j, k;
struct {
short row[MAXCELL];
short rowspan[MAXCELL];
char index[MAXCELL];
short maxcell;
short height[MAXCELL];
} cell;
int space;
cell.maxcell = -1;
for (j = 0; j <= t->maxrow; j++) {
if (!t->tabattr[j])
continue;
for (i = 0; i <= t->maxcol; i++) {
int t_dep, rowspan;
if (t->tabattr[j][i] & (HTT_X | HTT_Y))
continue;
if (t->tabdata[j][i] == NULL)
t_dep = 0;
else
t_dep = t->tabdata[j][i]->nitem;
rowspan = table_rowspan(t, j, i);
if (rowspan > 1) {
int c = cell.maxcell + 1;
k = bsearch_2short(rowspan, cell.rowspan,
j, cell.row, t->maxrow + 1,
cell.index, c);
if (k <= cell.maxcell) {
int idx = cell.index[k];
if (cell.row[idx] == j &&
cell.rowspan[idx] == rowspan)
c = idx;
}
if (c < MAXCELL) {
if (c > cell.maxcell) {
cell.maxcell++;
cell.row[cell.maxcell] = j;
cell.rowspan[cell.maxcell] = rowspan;
cell.height[cell.maxcell] = 0;
if (cell.maxcell > k)
bcopy(cell.index + k, cell.index + k + 1, cell.maxcell - k);
cell.index[k] = cell.maxcell;
}
if (cell.height[c] < t_dep)
cell.height[c] = t_dep;
}
continue;
}
if (t->tabheight[j] < t_dep)
t->tabheight[j] = t_dep;
}
}
switch (t->border_mode) {
case BORDER_THIN:
case BORDER_THICK:
case BORDER_NOWIN:
space = 1;
break;
case BORDER_NONE:
space = 0;
}
check_cell_width(t->tabheight, cell.height, cell.row, cell.rowspan,
cell.maxcell, cell.index, space, 1);
}
#define CHECK_MINIMUM 1
#define CHECK_FIXED 2
int
get_table_width(struct table *t, short *orgwidth, short *cellwidth,
int flag)
{
#ifdef __GNUC__
short newwidth[t->maxcol + 1];
#else /* not __GNUC__ */
short newwidth[MAXCOL];
#endif /* not __GNUC__ */
int i;
int swidth;
struct table_cell *cell = &t->cell;
int rulewidth = table_rule_width(t);
for (i = 0; i <= t->maxcol; i++)
newwidth[i] = max(orgwidth[i], 0);
if (flag & CHECK_FIXED) {
#ifdef __GNUC__
short ccellwidth[cell->maxcell + 1];
#else /* not __GNUC__ */
short ccellwidth[MAXCELL];
#endif /* not __GNUC__ */
for (i = 0; i <= t->maxcol; i++) {
if (newwidth[i] < t->fixed_width[i])
newwidth[i] = t->fixed_width[i];
}
for (i = 0; i <= cell->maxcell; i++) {
ccellwidth[i] = cellwidth[i];
if (ccellwidth[i] < cell->fixed_width[i])
ccellwidth[i] = cell->fixed_width[i];
}
check_cell_width(newwidth, ccellwidth, cell->col, cell->colspan,
cell->maxcell, cell->index, t->cellspacing, 0);
}
else {
check_cell_width(newwidth, cellwidth, cell->col, cell->colspan,
cell->maxcell, cell->index, t->cellspacing, 0);
}
if (flag & CHECK_MINIMUM)
check_minimum_width(t, newwidth);
swidth = 0;
for (i = 0; i <= t->maxcol; i++) {
swidth += ceil_at_intervals(newwidth[i], rulewidth);
}
swidth += table_border_width(t);
return swidth;
}
#define minimum_table_width(t)\
(get_table_width(t,t->minimum_width,t->cell.minimum_width,0))
#define maximum_table_width(t)\
(get_table_width(t,t->tabwidth,t->cell.width,CHECK_FIXED))
#define fixed_table_width(t)\
(get_table_width(t,t->fixed_width,t->cell.fixed_width,CHECK_MINIMUM))
void
renderCoTable(struct table *tbl, int maxlimit)
{
struct readbuffer obuf;
struct html_feed_environ h_env;
struct environment envs[MAX_ENV_LEVEL];
struct table *t;
int i, col, row;
int indent, maxwidth;
for (i = 0; i < tbl->ntable; i++) {
t = tbl->tables[i].ptr;
col = tbl->tables[i].col;
row = tbl->tables[i].row;
indent = tbl->tables[i].indent;
init_henv(&h_env, &obuf, envs, MAX_ENV_LEVEL, tbl->tables[i].buf,
get_spec_cell_width(tbl, row, col), indent);
check_row(tbl, row);
if (h_env.limit > maxlimit)
h_env.limit = maxlimit;
if (t->total_width == 0)
maxwidth = h_env.limit - indent;
else if (t->total_width > 0)
maxwidth = t->total_width;
else
maxwidth = t->total_width =
-t->total_width * h_env.limit / 100;
renderTable(t, maxwidth, &h_env);
}
}
static void
make_caption(struct table *t, struct html_feed_environ *h_env)
{
struct html_feed_environ henv;
struct readbuffer obuf;
struct environment envs[MAX_ENV_LEVEL];
int limit;
if (t->caption->length <= 0)
return;
if (t->total_width > 0)
limit = t->total_width;
else
limit = h_env->limit;
init_henv(&henv, &obuf, envs, MAX_ENV_LEVEL, newTextLineList(),
limit, h_env->envs[h_env->envc].indent);
HTMLlineproc1("<center>", &henv);
HTMLlineproc0(t->caption->ptr, &henv, FALSE);
HTMLlineproc1("</center>", &henv);
if (t->total_width < henv.maxlimit)
t->total_width = henv.maxlimit;
limit = h_env->limit;
h_env->limit = t->total_width;
HTMLlineproc1("<center>", h_env);
HTMLlineproc0(t->caption->ptr, h_env, FALSE);
HTMLlineproc1("</center>", h_env);
h_env->limit = limit;
}
void
renderTable(struct table *t,
int max_width,
struct html_feed_environ *h_env)
{
int i, j, w, r, h;
Str renderbuf;
short new_tabwidth[MAXCOL];
#ifdef MATRIX
int itr;
VEC *newwidth;
MAT *mat, *minv;
PERM *pivot;
#endif /* MATRIX */
int width;
int rulewidth;
Str vrulea, vruleb, vrulec;
#ifdef ID_EXT
Str idtag;
#endif /* ID_EXT */
t->total_height = 0;
if (t->maxcol < 0) {
make_caption(t, h_env);
return;
}
if (t->sloppy_width > max_width)
max_width = t->sloppy_width;
rulewidth = table_rule_width(t);
max_width -= table_border_width(t);
if (rulewidth > 1)
max_width = floor_at_intervals(max_width, rulewidth);
if (max_width < rulewidth)
max_width = rulewidth;
check_maximum_width(t);
#ifdef MATRIX
if (t->maxcol == 0) {
if (t->tabwidth[0] > max_width)
t->tabwidth[0] = max_width;
if (t->total_width > 0)
t->tabwidth[0] = max_width;
else if (t->fixed_width[0] > 0)
t->tabwidth[0] = t->fixed_width[0];
if (t->tabwidth[0] < t->minimum_width[0])
t->tabwidth[0] = t->minimum_width[0];
}
else {
set_table_matrix(t, max_width);
itr = 0;
mat = m_get(t->maxcol + 1, t->maxcol + 1);
pivot = px_get(t->maxcol + 1);
newwidth = v_get(t->maxcol + 1);
minv = m_get(t->maxcol + 1, t->maxcol + 1);
do {
m_copy(t->matrix, mat);
LUfactor(mat, pivot);
LUsolve(mat, pivot, t->vector, newwidth);
LUinverse(mat, pivot, minv);
#ifdef TABLE_DEBUG
set_integered_width(t, newwidth->ve, new_tabwidth);
fprintf(stderr, "itr=%d\n", itr);
fprintf(stderr, "max_width=%d\n", max_width);
fprintf(stderr, "minimum : ");
for (i = 0; i <= t->maxcol; i++)
fprintf(stderr, "%2d ", t->minimum_width[i]);
fprintf(stderr, "\nfixed : ");
for (i = 0; i <= t->maxcol; i++)
fprintf(stderr, "%2d ", t->fixed_width[i]);
fprintf(stderr, "\ndecided : ");
for (i = 0; i <= t->maxcol; i++)
fprintf(stderr, "%2d ", new_tabwidth[i]);
fprintf(stderr, "\n");
#endif /* TABLE_DEBUG */
itr++;
} while (check_table_width(t, newwidth->ve, minv, itr));
set_integered_width(t, newwidth->ve, new_tabwidth);
check_minimum_width(t, new_tabwidth);
v_free(newwidth);
px_free(pivot);
m_free(mat);
m_free(minv);
m_free(t->matrix);
v_free(t->vector);
for (i = 0; i <= t->maxcol; i++) {
t->tabwidth[i] = new_tabwidth[i];
}
}
#else /* not MATRIX */
set_table_width(t, new_tabwidth, max_width);
for (i = 0; i <= t->maxcol; i++) {
t->tabwidth[i] = new_tabwidth[i];
}
#endif /* not MATRIX */
check_minimum_width(t, t->tabwidth);
for (i = 0; i <= t->maxcol; i++)
t->tabwidth[i] = ceil_at_intervals(t->tabwidth[i], rulewidth);
renderCoTable(t, h_env->limit);
for (i = 0; i <= t->maxcol; i++) {
for (j = 0; j <= t->maxrow; j++) {
check_row(t, j);
if (t->tabattr[j][i] & HTT_Y)
continue;
do_refill(t, j, i, h_env->limit);
}
}
check_minimum_width(t, t->tabwidth);
t->total_width = 0;
for (i = 0; i <= t->maxcol; i++) {
t->tabwidth[i] = ceil_at_intervals(t->tabwidth[i], rulewidth);
t->total_width += t->tabwidth[i];
}
t->total_width += table_border_width(t);
check_table_height(t);
for (i = 0; i <= t->maxcol; i++) {
for (j = 0; j <= t->maxrow; j++) {
TextLineList *l;
int k;
if ((t->tabattr[j][i] & HTT_Y) ||
(t->tabattr[j][i] & HTT_TOP) ||
(t->tabdata[j][i] == NULL))
continue;
h = t->tabheight[j];
for (k = j + 1; k <= t->maxrow; k++) {
if (!(t->tabattr[k][i] & HTT_Y))
break;
h += t->tabheight[k];
switch (t->border_mode) {
case BORDER_THIN:
case BORDER_THICK:
h += 1;
break;
}
}
h -= t->tabdata[j][i]->nitem;
if (t->tabattr[j][i] & HTT_MIDDLE)
h /= 2;
if (h <= 0)
continue;
l = newTextLineList();
for (k = 0; k < h; k++)
pushTextLine(l, newTextLine(NULL, 0));
t->tabdata[j][i] = appendGeneralList((GeneralList *)l,
t->tabdata[j][i]);
}
}
/* table output */
width = t->total_width;
make_caption(t, h_env);
HTMLlineproc1("<pre for_table>", h_env);
#ifdef ID_EXT
if (t->id != NULL) {
idtag = Sprintf("<_id id=\"%s\">", html_quote((t->id)->ptr));
HTMLlineproc1(idtag->ptr, h_env);
}
#endif /* ID_EXT */
switch (t->border_mode) {
case BORDER_THIN:
case BORDER_THICK:
renderbuf = Strnew();
print_sep(t, -1, T_TOP, t->maxcol, renderbuf);
push_render_image(renderbuf, width, t->total_width, h_env);
t->total_height += 1;
break;
}
vruleb = Strnew();
switch (t->border_mode) {
case BORDER_THIN:
case BORDER_THICK:
vrulea = Strnew();
vrulec = Strnew();
Strcat_charp(vrulea, TK_VERTICALBAR(t->border_mode));
for (i = 0; i < t->cellpadding; i++) {
Strcat_char(vrulea, ' ');
Strcat_char(vruleb, ' ');
Strcat_char(vrulec, ' ');
}
Strcat_charp(vrulec, TK_VERTICALBAR(t->border_mode));
case BORDER_NOWIN:
#if defined(__EMX__)&&!defined(JP_CHARSET)
Strcat_charp(vruleb, CodePage==850?"\263":TN_VERTICALBAR);
#else
Strcat_charp(vruleb, TN_VERTICALBAR);
#endif
for (i = 0; i < t->cellpadding; i++)
Strcat_char(vruleb, ' ');
break;
case BORDER_NONE:
for (i = 0; i < t->cellspacing; i++)
Strcat_char(vruleb, ' ');
}
for (r = 0; r <= t->maxrow; r++) {
for (h = 0; h < t->tabheight[r]; h++) {
renderbuf = Strnew();
if (t->border_mode == BORDER_THIN || t->border_mode == BORDER_THICK)
Strcat(renderbuf, vrulea);
#ifdef ID_EXT
if (t->tridvalue[r] != NULL && h == 0) {
idtag = Sprintf("<_id id=\"%s\">",
html_quote((t->tridvalue[r])->ptr));
Strcat(renderbuf, idtag);
}
#endif /* ID_EXT */
for (i = 0; i <= t->maxcol; i++) {
check_row(t, r);
#ifdef ID_EXT
if (t->tabidvalue[r][i] != NULL && h == 0) {
idtag = Sprintf("<_id id=\"%s\">",
html_quote((t->tabidvalue[r][i])->ptr));
Strcat(renderbuf, idtag);
}
#endif /* ID_EXT */
if (!(t->tabattr[r][i] & HTT_X)) {
w = t->tabwidth[i];
for (j = i + 1;
j <= t->maxcol && (t->tabattr[r][j] & HTT_X);
j++)
w += t->tabwidth[j] + t->cellspacing;
if (t->tabattr[r][i] & HTT_Y) {
for (j = r - 1;
j >= 0 && t->tabattr[j] && (t->tabattr[j][i] & HTT_Y);
j--);
print_item(t, j, i, w, renderbuf);
}
else
print_item(t, r, i, w, renderbuf);
}
if (i < t->maxcol && !(t->tabattr[r][i + 1] & HTT_X))
Strcat(renderbuf, vruleb);
}
switch (t->border_mode) {
case BORDER_THIN:
case BORDER_THICK:
Strcat(renderbuf, vrulec);
t->total_height += 1;
break;
}
push_render_image(renderbuf, width, t->total_width, h_env);
}
if (r < t->maxrow && t->border_mode != BORDER_NONE) {
renderbuf = Strnew();
print_sep(t, r, T_MIDDLE, t->maxcol, renderbuf);
push_render_image(renderbuf, width, t->total_width, h_env);
}
t->total_height += t->tabheight[r];
}
switch (t->border_mode) {
case BORDER_THIN:
case BORDER_THICK:
renderbuf = Strnew();
print_sep(t, t->maxrow, T_BOTTOM, t->maxcol, renderbuf);
push_render_image(renderbuf, width, t->total_width, h_env);
t->total_height += 1;
break;
}
if (t->total_height == 0) {
renderbuf = Strnew(" ");
t->total_height++;
t->total_width = 1;
push_render_image(renderbuf, 1, t->total_width, h_env);
}
HTMLlineproc1("</pre>", h_env);
}
#ifdef TABLE_NO_COMPACT
#define THR_PADDING 2
#else
#define THR_PADDING 4
#endif
struct table *
begin_table(int border, int spacing, int padding, int vspace)
{
struct table *t;
int mincell = minimum_cellspacing(border);
int rcellspacing;
int mincell_pixels = round(mincell * pixel_per_char);
int ppc = round(pixel_per_char);
t = newTable();
t->row = t->col = -1;
t->maxcol = -1;
t->maxrow = -1;
t->border_mode = border;
t->flag = 0;
if (border == BORDER_NOWIN)
t->flag |= TBL_EXPAND_OK;
rcellspacing = spacing + 2 * padding;
switch (border) {
case BORDER_THIN:
case BORDER_THICK:
case BORDER_NOWIN:
t->cellpadding = padding - (mincell_pixels - 4) / 2;
break;
case BORDER_NONE:
t->cellpadding = rcellspacing - mincell_pixels;
}
if (t->cellpadding >= ppc)
t->cellpadding /= ppc;
else if (t->cellpadding > 0)
t->cellpadding = 1;
else
t->cellpadding = 0;
switch (border) {
case BORDER_THIN:
case BORDER_THICK:
case BORDER_NOWIN:
t->cellspacing = 2 * t->cellpadding + mincell;
break;
case BORDER_NONE:
t->cellspacing = t->cellpadding + mincell;
}
if (border == BORDER_NONE) {
if (rcellspacing / 2 + vspace <= 1)
t->vspace = 0;
else
t->vspace = 1;
}
else {
if (vspace < ppc)
t->vspace = 0;
else
t->vspace = 1;
}
if (border == BORDER_NONE) {
if (rcellspacing <= THR_PADDING)
t->vcellpadding = 0;
else
t->vcellpadding = 1;
}
else {
if (padding < 2 * ppc - 2)
t->vcellpadding = 0;
else
t->vcellpadding = 1;
}
return t;
}
void
end_table(struct table *tbl)
{
struct table_cell *cell = &tbl->cell;
int i, rulewidth = table_rule_width(tbl);
if (rulewidth > 1) {
if (tbl->total_width > 0)
tbl->total_width =
ceil_at_intervals(tbl->total_width, rulewidth);
for (i = 0; i <= tbl->maxcol; i++) {
tbl->minimum_width[i] =
ceil_at_intervals(tbl->minimum_width[i], rulewidth);
tbl->tabwidth[i] =
ceil_at_intervals(tbl->tabwidth[i], rulewidth);
if (tbl->fixed_width[i] > 0)
tbl->fixed_width[i] =
ceil_at_intervals(tbl->fixed_width[i], rulewidth);
}
for (i = 0; i <= cell->maxcell; i++) {
cell->minimum_width[i] =
ceil_at_intervals(cell->minimum_width[i], rulewidth);
cell->width[i] =
ceil_at_intervals(cell->width[i], rulewidth);
if (cell->fixed_width[i] > 0)
cell->fixed_width[i] =
ceil_at_intervals(cell->fixed_width[i], rulewidth);
}
}
tbl->sloppy_width = fixed_table_width(tbl);
if (tbl->total_width > tbl->sloppy_width)
tbl->sloppy_width = tbl->total_width;
}
static void
check_minimum0(struct table *t, int min)
{
int i, w, ww;
struct table_cell *cell;
if (t->col < 0)
return;
if (t->tabwidth[t->col] < 0)
return;
check_row(t, t->row);
w = table_colspan(t, t->row, t->col);
min += t->indent;
if (w == 1)
ww = min;
else {
cell = &t->cell;
ww = 0;
if (cell->icell >= 0 && cell->minimum_width[cell->icell] < min)
cell->minimum_width[cell->icell] = min;
}
for (i = t->col;
i <= t->maxcol && (i == t->col || (t->tabattr[t->row][i] & HTT_X));
i++) {
if (t->minimum_width[i] < ww)
t->minimum_width[i] = ww;
}
}
static int
setwidth0(struct table *t, struct table_mode *mode)
{
int w;
int width = t->tabcontentssize;
struct table_cell *cell = &t->cell;
if (t->col < 0)
return -1;
if (t->tabwidth[t->col] < 0)
return -1;
check_row(t, t->row);
if (t->linfo.prev_spaces > 0)
width -= t->linfo.prev_spaces;
w = table_colspan(t, t->row, t->col);
if (w == 1) {
if (t->tabwidth[t->col] < width)
t->tabwidth[t->col] = width;
}
else if (cell->icell >= 0) {
if (cell->width[cell->icell] < width)
cell->width[cell->icell] = width;
}
return width;
}
static void
setwidth(struct table *t, struct table_mode *mode)
{
int width = setwidth0(t, mode);
if (width < 0)
return;
#ifdef NOWRAP
if (t->tabattr[t->row][t->col] & HTT_NOWRAP)
check_minimum0(t, width);
#endif /* NOWRAP */
if (mode->pre_mode & (TBLM_NOBR | TBLM_PRE | TBLM_PRE_INT) &&
mode->nobr_offset >= 0)
check_minimum0(t, width - mode->nobr_offset);
}
static void
addcontentssize(struct table *t, int width)
{
if (t->col < 0)
return;
if (t->tabwidth[t->col] < 0)
return;
check_row(t, t->row);
t->tabcontentssize += width;
}
static void table_close_anchor0(struct table *tbl, struct table_mode *mode);
static void
clearcontentssize(struct table *t, struct table_mode *mode)
{
table_close_anchor0(t, mode);
mode->nobr_offset = 0;
t->linfo.prev_spaces = -1;
t->linfo.prevchar = ' ';
t->linfo.prev_ctype = PC_ASCII;
t->linfo.length = 0;
t->tabcontentssize = 0;
}
static void
begin_cell(struct table *t, struct table_mode *mode)
{
clearcontentssize(t, mode);
mode->indent_level = 0;
mode->nobr_level = 0;
mode->pre_mode = 0;
t->indent = 0;
t->flag |= TBL_IN_COL;
if (t->suspended_data) {
check_row(t, t->row);
if (t->tabdata[t->row][t->col] == NULL)
t->tabdata[t->row][t->col] = newGeneralList();
appendGeneralList(t->tabdata[t->row][t->col],
(GeneralList *)t->suspended_data);
t->suspended_data = NULL;
}
}
void
check_rowcol(struct table *tbl, struct table_mode *mode)
{
int row = tbl->row, col = tbl->col;
if (!(tbl->flag & TBL_IN_ROW)) {
tbl->flag |= TBL_IN_ROW;
tbl->row++;
if (tbl->row > tbl->maxrow)
tbl->maxrow = tbl->row;
tbl->col = -1;
}
if (tbl->row == -1)
tbl->row = 0;
if (tbl->col == -1)
tbl->col = 0;
for (;; tbl->row++) {
check_row(tbl, tbl->row);
for (; tbl->col < MAXCOL &&
tbl->tabattr[tbl->row][tbl->col] & (HTT_X | HTT_Y);
tbl->col++);
if (tbl->col < MAXCOL)
break;
tbl->col = 0;
}
if (tbl->row > tbl->maxrow)
tbl->maxrow = tbl->row;
if (tbl->col > tbl->maxcol)
tbl->maxcol = tbl->col;
if (tbl->row != row || tbl->col != col)
begin_cell(tbl, mode);
tbl->flag |= TBL_IN_COL;
}
int
skip_space(struct table *t, char *line, struct table_linfo *linfo,
int checkminimum)
{
int skip = 0, s = linfo->prev_spaces;
Lineprop ctype, prev_ctype = linfo->prev_ctype;
int prevchar = linfo->prevchar;
int w = linfo->length;
int min = 1;
if (*line == '<' && line[strlen(line) - 1] == '>') {
if (checkminimum)
check_minimum0(t, visible_length(line));
return 0;
}
while (*line) {
char *save = line;
int ec = '\0', len, wlen, c;
ctype = get_mctype(line);
wlen = len = get_mclen(ctype);
c = mctowc(line, ctype);
if (min < w)
min = w;
if (ctype == PC_ASCII && IS_SPACE(c)) {
w = 0;
s++;
}
else {
if (c == '&') {
ec = getescapechar(&line);
if (ec >= 0) {
c = ec;
ctype = IS_CNTRL(ec) ? PC_CTRL : PC_ASCII;
len = strlen(conv_entity(ec));
wlen = line - save;
}
}
if (prevchar && is_boundary(prevchar, c)) {
w = len;
}
else {
w += len;
}
if (s > 0) {
#ifdef JP_CHARSET
if (ctype == PC_KANJI && prev_ctype == PC_KANJI)
skip += s;
else
#endif /* JP_CHARSET */
skip += s - 1;
}
s = 0;
prev_ctype = ctype;
}
prevchar = c;
line = save + wlen;
}
if (s > 1) {
skip += s - 1;
linfo->prev_spaces = 1;
}
else {
linfo->prev_spaces = s;
}
linfo->prev_ctype = prev_ctype;
linfo->prevchar = prevchar;
if (checkminimum) {
if (min < w)
min = w;
linfo->length = w;
check_minimum0(t, min);
}
return skip;
}
static void
feed_table_inline_tag(struct table *tbl,
char *line,
struct table_mode *mode,
int width)
{
check_rowcol(tbl, mode);
pushdata(tbl, tbl->row, tbl->col, line);
if (width >= 0) {
check_minimum0(tbl, width);
addcontentssize(tbl, width);
setwidth(tbl, mode);
}
}
static void
feed_table_block_tag(struct table *tbl,
char *line,
struct table_mode *mode,
int indent,
int cmd)
{
int offset;
if (mode->indent_level <= 0 && indent == -1)
return;
setwidth(tbl, mode);
feed_table_inline_tag(tbl, line, mode, -1);
clearcontentssize(tbl, mode);
if (indent == 1) {
mode->indent_level++;
if (mode->indent_level <= MAX_INDENT_LEVEL)
tbl->indent += INDENT_INCR;
}
else if (indent == -1) {
mode->indent_level--;
if (mode->indent_level < MAX_INDENT_LEVEL)
tbl->indent -= INDENT_INCR;
}
offset = tbl->indent;
if (cmd == HTML_DT) {
if (mode->indent_level > 0 && mode->indent_level <= MAX_INDENT_LEVEL)
offset -= INDENT_INCR;
}
if (tbl->indent > 0) {
check_minimum0(tbl, 0);
addcontentssize(tbl, offset);
}
}
static void
table_close_select(struct table *tbl, struct table_mode *mode, int width)
{
Str tmp = process_n_select();
mode->pre_mode &= ~TBLM_INSELECT;
feed_table1(tbl, tmp, mode, width);
}
static void
table_close_textarea(struct table *tbl, struct table_mode *mode, int width)
{
Str tmp = process_n_textarea();
mode->pre_mode &= ~TBLM_INTXTA;
feed_table1(tbl, tmp, mode, width);
}
static void
table_close_anchor0(struct table *tbl, struct table_mode *mode)
{
if (!(mode->pre_mode & TBLM_ANCHOR))
return;
mode->pre_mode &= ~TBLM_ANCHOR;
if (tbl->tabcontentssize == mode->anchor_offset) {
check_minimum0(tbl, 1);
addcontentssize(tbl, 1);
setwidth(tbl, mode);
}
else if (tbl->linfo.prev_spaces > 0 &&
tbl->tabcontentssize - 1 == mode->anchor_offset) {
if(tbl->linfo.prev_spaces > 0)
tbl->linfo.prev_spaces = -1;
}
}
#define TAG_ACTION_NONE 0
#define TAG_ACTION_FEED 1
#define TAG_ACTION_TABLE 2
#define TAG_ACTION_N_TABLE 3
#define CASE_TABLE_TAG \
case HTML_TABLE:\
case HTML_N_TABLE:\
case HTML_TR:\
case HTML_N_TR:\
case HTML_TD:\
case HTML_N_TD:\
case HTML_TH:\
case HTML_N_TH:\
case HTML_THEAD:\
case HTML_N_THEAD:\
case HTML_TBODY:\
case HTML_N_TBODY:\
case HTML_TFOOT:\
case HTML_N_TFOOT:\
case HTML_COLGROUP:\
case HTML_N_COLGROUP:\
case HTML_COL
static int
feed_table_tag(struct table *tbl, char *line, struct table_mode *mode, int width, struct parsed_tag *tag)
{
int cmd;
char *p;
struct table_cell *cell = &tbl->cell;
int colspan, rowspan;
int col, prev_col;
int i, j, k, v, v0, w, id;
Str tok, tmp, anchor;
table_attr align, valign;
cmd = tag->tagid;
if (mode->pre_mode & TBLM_IGNORE) {
switch (cmd) {
case HTML_N_STYLE:
mode->pre_mode &= ~TBLM_STYLE;
return TAG_ACTION_NONE;
case HTML_N_SCRIPT:
mode->pre_mode &= ~TBLM_SCRIPT;
return TAG_ACTION_NONE;
default:
return TAG_ACTION_NONE;
}
}
switch (cmd) {
CASE_TABLE_TAG:
if (mode->caption)
mode->caption = 0;
if (mode->pre_mode & (TBLM_IGNORE|TBLM_XMP|TBLM_LST))
mode->pre_mode &= ~(TBLM_IGNORE|TBLM_XMP|TBLM_LST);
if (mode->pre_mode & TBLM_INTXTA)
table_close_textarea(tbl, mode, width);
if (mode->pre_mode & TBLM_INSELECT)
table_close_select(tbl, mode, width);
}
if (mode->caption) {
switch (cmd) {
case HTML_N_CAPTION:
mode->caption = 0;
return TAG_ACTION_NONE;
default:
return TAG_ACTION_FEED;
}
}
/* failsafe: a tag other than <option></option>and </select> in *
* <select> environment is regarded as the end of <select>. */
if (mode->pre_mode & TBLM_INSELECT && cmd == HTML_N_FORM) {
table_close_select(tbl, mode, width);
}
if (
(mode->pre_mode & TBLM_INSELECT && cmd != HTML_N_SELECT) ||
(mode->pre_mode & TBLM_INTXTA && cmd != HTML_N_TEXTAREA) ||
(mode->pre_mode & TBLM_XMP && cmd != HTML_N_XMP) ||
(mode->pre_mode & TBLM_LST && cmd != HTML_N_LISTING))
return TAG_ACTION_FEED;
if (mode->pre_mode & TBLM_PRE) {
switch (cmd) {
case HTML_NOBR:
case HTML_N_NOBR:
case HTML_PRE_INT:
case HTML_N_PRE_INT:
return TAG_ACTION_NONE;
}
}
switch (cmd) {
case HTML_TABLE:
check_rowcol(tbl, mode);
return TAG_ACTION_TABLE;
case HTML_N_TABLE:
if (tbl->suspended_data)
check_rowcol(tbl, mode);
return TAG_ACTION_N_TABLE;
case HTML_TR:
if (tbl->col >= 0 && tbl->tabcontentssize > 0)
setwidth(tbl, mode);
tbl->col = -1;
tbl->row++;
tbl->flag |= TBL_IN_ROW;
tbl->flag &= ~TBL_IN_COL;
align = 0;
valign = 0;
if (parsedtag_get_value(tag, ATTR_ALIGN, &i)) {
switch (i) {
case ALIGN_LEFT:
align = (HTT_LEFT | HTT_TRSET);
break;
case ALIGN_RIGHT:
align = (HTT_RIGHT | HTT_TRSET);
break;
case ALIGN_CENTER:
align = (HTT_CENTER | HTT_TRSET);
break;
}
}
if (parsedtag_get_value(tag, ATTR_VALIGN, &i)) {
switch (i) {
case VALIGN_TOP:
valign = (HTT_TOP | HTT_VTRSET);
break;
case VALIGN_MIDDLE:
valign = (HTT_MIDDLE | HTT_VTRSET);
break;
case VALIGN_BOTTOM:
valign = (HTT_BOTTOM | HTT_VTRSET);
break;
}
}
#ifdef ID_EXT
if (parsedtag_get_value(tag, ATTR_ID, &p))
tbl->tridvalue[tbl->row] = Strnew_charp(p);
#endif /* ID_EXT */
tbl->trattr = align | valign;
break;
case HTML_TH:
case HTML_TD:
prev_col = tbl->col;
if (tbl->col >= 0 && tbl->tabcontentssize > 0)
setwidth(tbl, mode);
if (tbl->row == -1) {
/* for broken HTML... */
tbl->row = -1;
tbl->col = -1;
tbl->maxrow = tbl->row;
}
if (tbl->col == -1) {
if (!(tbl->flag & TBL_IN_ROW)) {
tbl->row++;
tbl->flag |= TBL_IN_ROW;
}
if (tbl->row > tbl->maxrow)
tbl->maxrow = tbl->row;
}
tbl->col++;
check_row(tbl, tbl->row);
while (tbl->tabattr[tbl->row][tbl->col]) {
tbl->col++;
}
if (tbl->col > MAXCOL - 1) {
tbl->col = prev_col;
return TAG_ACTION_NONE;
}
if (tbl->col > tbl->maxcol) {
tbl->maxcol = tbl->col;
}
colspan = rowspan = 1;
if (tbl->trattr & HTT_TRSET)
align = (tbl->trattr & HTT_ALIGN);
else if (cmd == HTML_TH)
align = HTT_CENTER;
else
align = HTT_LEFT;
if (tbl->trattr & HTT_VTRSET)
valign = (tbl->trattr & HTT_VALIGN);
else
valign = HTT_MIDDLE;
if (parsedtag_get_value(tag, ATTR_ROWSPAN, &rowspan)) {
if ((tbl->row + rowspan) >= tbl->max_rowsize)
check_row(tbl, tbl->row + rowspan);
}
if (parsedtag_get_value(tag, ATTR_COLSPAN, &colspan)) {
if ((tbl->col + colspan) >= MAXCOL) {
/* Can't expand column */
colspan = MAXCOL - tbl->col;
}
}
if (parsedtag_get_value(tag, ATTR_ALIGN, &i)) {
switch (i) {
case ALIGN_LEFT:
align = HTT_LEFT;
break;
case ALIGN_RIGHT:
align = HTT_RIGHT;
break;
case ALIGN_CENTER:
align = HTT_CENTER;
break;
}
}
if (parsedtag_get_value(tag, ATTR_VALIGN, &i)) {
switch (i) {
case VALIGN_TOP:
valign = HTT_TOP;
break;
case VALIGN_MIDDLE:
valign = HTT_MIDDLE;
break;
case VALIGN_BOTTOM:
valign = HTT_BOTTOM;
break;
}
}
#ifdef NOWRAP
if (parsedtag_exists(tag, ATTR_NOWRAP))
tbl->tabattr[tbl->row][tbl->col] |= HTT_NOWRAP;
#endif /* NOWRAP */
v = 0;
if (parsedtag_get_value(tag, ATTR_WIDTH, &v)) {
#ifdef TABLE_EXPAND
if (v > 0) {
if (tbl->real_width > 0)
v = - (v * 100) / (tbl->real_width * pixel_per_char);
else
v = (int)(v / pixel_per_char);
}
#else
v = RELATIVE_WIDTH(v);
#endif /* not TABLE_EXPAND */
}
#ifdef ID_EXT
if (parsedtag_get_value(tag, ATTR_ID, &p))
tbl->tabidvalue[tbl->row][tbl->col] = Strnew_charp(p);
#endif /* ID_EXT */
#ifdef NOWRAP
if (v != 0) {
/* NOWRAP and WIDTH= conflicts each other */
tbl->tabattr[tbl->row][tbl->col] &= ~HTT_NOWRAP;
}
#endif /* NOWRAP */
tbl->tabattr[tbl->row][tbl->col] &= ~(HTT_ALIGN | HTT_VALIGN);
tbl->tabattr[tbl->row][tbl->col] |= (align | valign);
if (colspan > 1) {
col = tbl->col;
cell->icell = cell->maxcell + 1;
k = bsearch_2short(colspan, cell->colspan, col, cell->col, MAXCOL,
cell->index, cell->icell);
if (k <= cell->maxcell) {
i = cell->index[k];
if (cell->col[i] == col &&
cell->colspan[i] == colspan)
cell->icell = i;
}
if (cell->icell > cell->maxcell && cell->icell < MAXCELL) {
cell->maxcell++;
cell->col[cell->maxcell] = col;
cell->colspan[cell->maxcell] = colspan;
cell->width[cell->maxcell] = 0;
cell->minimum_width[cell->maxcell] = 0;
cell->fixed_width[cell->maxcell] = 0;
if (cell->maxcell > k)
bcopy(cell->index + k, cell->index + k + 1, cell->maxcell - k);
cell->index[k] = cell->maxcell;
}
if (cell->icell > cell->maxcell)
cell->icell = -1;
}
if (v != 0) {
if (colspan == 1) {
v0 = tbl->fixed_width[tbl->col];
if (v0 == 0 || (v0 > 0 && v > v0) || (v0 < 0 && v < v0)) {
#ifdef FEED_TABLE_DEBUG
fprintf(stderr, "width(%d) = %d\n", tbl->col, v);
#endif /* TABLE_DEBUG */
tbl->fixed_width[tbl->col] = v;
}
}
else if (cell->icell >= 0) {
v0 = cell->fixed_width[cell->icell];
if (v0 == 0 || (v0 > 0 && v > v0) || (v0 < 0 && v < v0))
cell->fixed_width[cell->icell] = v;
}
}
for (i = 0; i < rowspan; i++) {
check_row(tbl, tbl->row + i);
for (j = 0; j < colspan; j++) {
tbl->tabattr[tbl->row + i][tbl->col + j] &= ~(HTT_X | HTT_Y);
tbl->tabattr[tbl->row + i][tbl->col + j] |=
((i > 0) ? HTT_Y : 0) | ((j > 0) ? HTT_X : 0);
if (tbl->col + j > tbl->maxcol) {
tbl->maxcol = tbl->col + j;
}
}
if (tbl->row + i > tbl->maxrow) {
tbl->maxrow = tbl->row + i;
}
}
begin_cell(tbl, mode);
break;
case HTML_N_TR:
setwidth(tbl, mode);
tbl->col = -1;
tbl->flag &= ~(TBL_IN_ROW | TBL_IN_COL);
return TAG_ACTION_NONE;
case HTML_N_TH:
case HTML_N_TD:
setwidth(tbl, mode);
tbl->flag &= ~TBL_IN_COL;
#ifdef FEED_TABLE_DEBUG
{
TextListItem *it;
int i = tbl->col, j = tbl->row;
fprintf(stderr, "(a) row,col: %d, %d\n", j, i);
if (tbl->tabdata[j] && tbl->tabdata[j][i]) {
for (it = ((TextList *)tbl->tabdata[j][i])->first;
it; it = it->next)
fprintf(stderr, " [%s] \n", it->ptr);
}
}
#endif
return TAG_ACTION_NONE;
case HTML_P:
case HTML_BR:
case HTML_DT:
case HTML_DD:
case HTML_CENTER:
case HTML_N_CENTER:
case HTML_DIV:
case HTML_N_DIV:
case HTML_H:
case HTML_N_H:
case HTML_LI:
case HTML_PRE:
case HTML_N_PRE:
case HTML_LISTING:
case HTML_N_LISTING:
case HTML_XMP:
case HTML_N_XMP:
case HTML_PLAINTEXT:
feed_table_block_tag(tbl, line, mode, 0, cmd);
switch (cmd) {
case HTML_PRE:
mode->pre_mode |= TBLM_PRE;
break;
case HTML_N_PRE:
mode->pre_mode &= ~TBLM_PRE;
break;
case HTML_LISTING:
mode->pre_mode |= TBLM_LST;
break;
case HTML_N_LISTING:
mode->pre_mode &= ~TBLM_LST;
break;
case HTML_XMP:
mode->pre_mode |= TBLM_XMP;
break;
case HTML_N_XMP:
mode->pre_mode &= ~TBLM_XMP;
break;
case HTML_PLAINTEXT:
mode->pre_mode |= TBLM_PLAINTEXT;
break;
}
break;
case HTML_DL:
case HTML_BLQ:
case HTML_OL:
case HTML_UL:
feed_table_block_tag(tbl, line, mode, 1, cmd);
break;
case HTML_N_DL:
case HTML_N_BLQ:
case HTML_N_OL:
case HTML_N_UL:
feed_table_block_tag(tbl, line, mode, -1, cmd);
break;
case HTML_PRE_INT:
case HTML_NOBR:
case HTML_WBR:
feed_table_inline_tag(tbl, line, mode, -1);
switch (cmd) {
case HTML_NOBR:
mode->nobr_level++;
if (mode->pre_mode & TBLM_NOBR)
return TAG_ACTION_NONE;
mode->pre_mode |= TBLM_NOBR;
break;
case HTML_PRE_INT:
if (mode->pre_mode & TBLM_PRE_INT)
return TAG_ACTION_NONE;
mode->pre_mode |= TBLM_PRE_INT;
tbl->linfo.prev_spaces = 0;
break;
}
mode->nobr_offset = -1;
if (tbl->linfo.length > 0) {
check_minimum0(tbl, tbl->linfo.length);
tbl->linfo.length = 0;
}
break;
case HTML_N_NOBR:
feed_table_inline_tag(tbl, line, mode, -1);
if (mode->nobr_level > 0)
mode->nobr_level--;
if (mode->nobr_level == 0)
mode->pre_mode &= ~TBLM_NOBR;
break;
case HTML_N_PRE_INT:
feed_table_inline_tag(tbl, line, mode, -1);
mode->pre_mode &= ~TBLM_PRE_INT;
break;
case HTML_IMG:
tok = process_img(tag);
feed_table1(tbl, tok, mode, width);
break;
case HTML_FORM:
process_form(tag);
break;
case HTML_N_FORM:
process_n_form();
break;
case HTML_INPUT:
tmp = process_input(tag);
feed_table1(tbl, tmp, mode, width);
break;
case HTML_SELECT:
process_select(tag);
mode->pre_mode |= TBLM_INSELECT;
break;
case HTML_N_SELECT:
table_close_select(tbl, mode, width);
break;
case HTML_OPTION:
/* nothing */
break;
case HTML_TEXTAREA:
w = 0;
check_rowcol(tbl, mode);
if (tbl->col + 1 <= tbl->maxcol &&
tbl->tabattr[tbl->row][tbl->col + 1] & HTT_X) {
if (cell->icell >= 0 && cell->fixed_width[cell->icell] > 0)
w = cell->fixed_width[cell->icell];
}
else {
if (tbl->fixed_width[tbl->col] > 0)
w = tbl->fixed_width[tbl->col];
}
tmp = process_textarea(tag, w);
feed_table1(tbl, tmp, mode, width);
mode->pre_mode |= TBLM_INTXTA;
break;
case HTML_N_TEXTAREA:
table_close_textarea(tbl, mode, width);
break;
case HTML_A:
table_close_anchor0(tbl, mode);
anchor = NULL;
i = 0;
parsedtag_get_value(tag, ATTR_HREF, &anchor);
parsedtag_get_value(tag, ATTR_HSEQ, &i);
if (anchor) {
check_rowcol(tbl, mode);
if (i == 0) {
Str tmp = process_anchor(tag, line);
pushdata(tbl, tbl->row, tbl->col, tmp->ptr);
}
else
pushdata(tbl, tbl->row, tbl->col, line);
if (i >= 0) {
mode->pre_mode |= TBLM_ANCHOR;
mode->anchor_offset = tbl->tabcontentssize;
}
}
else
suspend_or_pushdata(tbl, line);
break;
case HTML_DEL:
case HTML_N_DEL:
case HTML_INS:
case HTML_N_INS:
feed_table_inline_tag(tbl, line, mode, 5);
break;
case HTML_TABLE_ALT:
id = -1;
w = 0;
parsedtag_get_value(tag, ATTR_TID, &id);
if (id >= 0 && id < tbl->ntable) {
struct table *tbl1 = tbl->tables[id].ptr;
feed_table_block_tag(tbl, line, mode, 0, cmd);
addcontentssize(tbl, maximum_table_width(tbl1));
check_minimum0(tbl, tbl1->sloppy_width);
#ifdef TABLE_EXPAND
w = tbl1->total_width;
v = 0;
colspan = table_colspan(tbl, tbl->row, tbl->col);
if (colspan > 1) {
if (cell->icell >= 0)
v = cell->fixed_width[cell->icell];
}
else
v = tbl->fixed_width[tbl->col];
if (v < 0 && tbl->real_width > 0 && tbl1->real_width > 0)
w = - (tbl1->real_width * 100) / tbl->real_width;
else
w = tbl1->real_width;
if (w > 0)
check_minimum0(tbl, w);
else if (w < 0 && v < w) {
if (colspan > 1) {
if (cell->icell >= 0)
cell->fixed_width[cell->icell] = w;
}
else
tbl->fixed_width[tbl->col] = w;
}
#endif
setwidth0(tbl, mode);
clearcontentssize(tbl, mode);
}
break;
case HTML_CAPTION:
mode->caption = 1;
break;
case HTML_THEAD:
case HTML_N_THEAD:
case HTML_TBODY:
case HTML_N_TBODY:
case HTML_TFOOT:
case HTML_N_TFOOT:
case HTML_COLGROUP:
case HTML_N_COLGROUP:
case HTML_COL:
break;
case HTML_SCRIPT:
mode->pre_mode |= TBLM_SCRIPT;
break;
case HTML_STYLE:
mode->pre_mode |= TBLM_STYLE;
break;
case HTML_N_A:
table_close_anchor0(tbl, mode);
case HTML_FONT:
case HTML_N_FONT:
case HTML_NOP:
suspend_or_pushdata(tbl, line);
break;
case HTML_FORM_INT:
case HTML_N_FORM_INT:
case HTML_INPUT_ALT:
case HTML_N_INPUT_ALT:
case HTML_IMG_ALT:
case HTML_EOL:
case HTML_RULE:
case HTML_N_RULE:
default:
/* unknown tag: put into table */
return TAG_ACTION_FEED;
}
return TAG_ACTION_NONE;
}
int
feed_table(struct table *tbl, char *line, struct table_mode *mode,
int width, int internal)
{
int i;
char *p;
Str tmp;
struct table_linfo *linfo = &tbl->linfo;
if (*line == '<') {
int action;
struct parsed_tag *tag;
p = line;
tag = parse_tag(&p, internal);
if (tag) {
action = feed_table_tag(tbl, line, mode, width, tag);
if (action == TAG_ACTION_NONE)
return -1;
else if (action == TAG_ACTION_N_TABLE)
return 0;
else if (action == TAG_ACTION_TABLE) {
return 1;
}
else if (parsedtag_need_reconstruct(tag))
line = parsedtag2str(tag)->ptr;
}
else {
if (!(mode->pre_mode & TBLM_PLAIN))
return -1;
}
}
if (mode->caption) {
Strcat_charp(tbl->caption, line);
return -1;
}
if (mode->pre_mode & TBLM_IGNORE)
return -1;
if (mode->pre_mode & TBLM_INTXTA) {
feed_textarea(line);
return -1;
}
if (mode->pre_mode & TBLM_INSELECT) {
feed_select(line);
return -1;
}
if (!(mode->pre_mode & TBLM_PLAIN) &&
!(*line == '<' && line[strlen(line) - 1] == '>') &&
strchr(line, '&') != NULL) {
tmp = Strnew();
for (p = line; *p;) {
char *q, *r;
if (*p == '&') {
if (!strncasecmp(p, "&", 5) ||
!strncasecmp(p, ">", 4) ||
!strncasecmp(p, "<", 4)) {
/* do not convert */
Strcat_char(tmp, *p);
p++;
}
else {
int ec;
q = p;
switch (ec = getescapechar(&p)) {
case '<':
Strcat_charp(tmp, "<");
break;
case '>':
Strcat_charp(tmp, ">");
break;
case '&':
Strcat_charp(tmp, "&");
break;
case '\r':
Strcat_char(tmp, '\n');
break;
default:
r = conv_entity(ec);
if (r != NULL && strlen(r) == 1 &&
ec == (unsigned char)*r) {
Strcat_char(tmp, *r);
break;
}
case -1:
Strcat_char(tmp, *q);
p = q + 1;
break;
}
}
}
else {
Strcat_char(tmp, *p);
p++;
}
}
line = tmp->ptr;
}
if (!(mode->pre_mode & TBLM_SPECIAL)) {
if (!(tbl->flag & TBL_IN_COL) || linfo->prev_spaces != 0)
while (IS_SPACE(*line))
line++;
if (*line == '\0')
return -1;
check_rowcol(tbl, mode);
if (mode->pre_mode & TBLM_NOBR && mode->nobr_offset < 0)
mode->nobr_offset = tbl->tabcontentssize;
/* count of number of spaces skipped in normal mode */
i = skip_space(tbl, line, linfo, !(mode->pre_mode & TBLM_NOBR));
addcontentssize(tbl, visible_length(line) - i);
setwidth(tbl, mode);
}
else {
/* <pre> mode or something like it */
check_rowcol(tbl, mode);
if (mode->pre_mode & TBLM_PRE_INT && mode->nobr_offset < 0)
mode->nobr_offset = tbl->tabcontentssize;
if (mode->pre_mode & TBLM_PLAIN)
i = strlen(line);
else
i = maximum_visible_length(line);
addcontentssize(tbl, i);
setwidth(tbl, mode);
if (!(mode->pre_mode & TBLM_PRE_INT)) {
p = line + strlen(line) - 1;
if (*p == '\r' || *p == '\n')
clearcontentssize(tbl, mode);
}
}
pushdata(tbl, tbl->row, tbl->col, line);
return -1;
}
void
feed_table1(struct table *tbl, Str tok, struct table_mode *mode, int width)
{
Str tokbuf;
int status;
char *line;
if (!tok)
return;
tokbuf = Strnew();
status = R_ST_NORMAL;
line = tok->ptr;
while (read_token(tokbuf, &line, &status, mode->pre_mode & TBLM_PREMODE, 0))
feed_table(tbl, tokbuf->ptr, mode, width, TRUE);
}
void
pushTable(struct table *tbl, struct table *tbl1)
{
int col;
int row;
col = tbl->col;
row = tbl->row;
if (tbl->ntable >= tbl->tables_size) {
struct table_in *tmp;
tbl->tables_size += MAX_TABLE_N;
tmp = New_N(struct table_in, tbl->tables_size);
if (tbl->tables)
#ifdef __CYGWIN__
bcopy((const char *) tbl->tables, (char *) tmp, (size_t) tbl->ntable * sizeof(struct table_in));
#else /* not __CYGWIN__ */
bcopy(tbl->tables, tmp, tbl->ntable * sizeof(struct table_in));
#endif /* not __CYGWIN__ */
tbl->tables = tmp;
}
tbl->tables[tbl->ntable].ptr = tbl1;
tbl->tables[tbl->ntable].col = col;
tbl->tables[tbl->ntable].row = row;
tbl->tables[tbl->ntable].indent = tbl->indent;
tbl->tables[tbl->ntable].buf = newTextLineList();
check_row(tbl, row);
if (col + 1 <= tbl->maxcol &&
tbl->tabattr[row][col + 1] & HTT_X)
tbl->tables[tbl->ntable].cell = tbl->cell.icell;
else
tbl->tables[tbl->ntable].cell = -1;
tbl->ntable++;
}
#ifdef MATRIX
int
correct_table_matrix(struct table *t, int col, int cspan, int a, double b)
{
int i, j;
int ecol = col + cspan;
double w = 1. / (b * b);
for (i = col; i < ecol; i++) {
v_add_val(t->vector, i, w * a);
for (j = i; j < ecol; j++) {
m_add_val(t->matrix, i, j, w);
m_set_val(t->matrix, j, i, m_entry(t->matrix, i, j));
}
}
return i;
}
static void
correct_table_matrix2(struct table *t, int col, int cspan, double s, double b)
{
int i, j;
int ecol = col + cspan;
int size = t->maxcol + 1;
double w = 1. / (b * b);
double ss;
for (i = 0; i < size; i++) {
for (j = i; j < size; j++) {
if (i >= col && i < ecol && j >= col && j < ecol)
ss = (1. - s) * (1. - s);
else if ((i >= col && i < ecol) || (j >= col && j < ecol))
ss = -(1. - s) * s;
else
ss = s * s;
m_add_val(t->matrix, i, j, w * ss);
}
}
}
static void
correct_table_matrix3(struct table *t, int col, char *flags, double s, double b)
{
int i, j;
double ss;
int size = t->maxcol + 1;
double w = 1. / (b * b);
int flg = (flags[col] == 0);
for (i = 0; i < size; i++) {
if (!((flg && flags[i] == 0) || (!flg && flags[i] != 0)))
continue;
for (j = i; j < size; j++) {
if (!((flg && flags[j] == 0) || (!flg && flags[j] != 0)))
continue;
if (i == col && j == col)
ss = (1. - s) * (1. - s);
else if (i == col || j == col)
ss = -(1. - s) * s;
else
ss = s * s;
m_add_val(t->matrix, i, j, w * ss);
}
}
}
static void
set_table_matrix0(struct table *t, int maxwidth)
{
int size = t->maxcol + 1;
int i, j, k, bcol, ecol;
int swidth, width, a;
double w0, w1, w, s, b;
#ifdef __GNUC__
double we[size];
char expand[size];
#else /* not __GNUC__ */
double we[MAXCOL];
char expand[MAXCOL];
#endif /* not __GNUC__ */
struct table_cell *cell = &t->cell;
w0 = 0.;
for (i = 0; i < size; i++) {
we[i] = weight(t->tabwidth[i]);
w0 += we[i];
}
if (w0 <= 0.)
w0 = 1.;
if (cell->necell == 0) {
for (i = 0; i < size; i++) {
s = we[i] / w0;
b = sigma_td_nw((int) (s * maxwidth));
correct_table_matrix2(t, i, 1, s, b);
}
return;
}
bzero(expand, size);
for (k = 0; k < cell->necell; k++) {
j = cell->eindex[k];
bcol = cell->col[j];
ecol = bcol + cell->colspan[j];
swidth = 0;
for (i = bcol; i < ecol; i++) {
swidth += t->tabwidth[i];
expand[i]++;
}
width = cell->width[j] - (cell->colspan[j] - 1) * t->cellspacing;
w = weight(width);
w1 = 0.;
for (i = bcol; i < ecol; i++)
w1 += we[i];
s = w / (w0 + w - w1);
a = (int) (s * maxwidth);
b = sigma_td_nw(a);
correct_table_matrix2(t, bcol, cell->colspan[j], s, b);
}
w1 = 0.;
for (i = 0; i < size; i++)
if (expand[i] == 0)
w1 += we[i];
for (i = 0; i < size; i++) {
if (expand[i] == 0) {
s = we[i] / max(w1, 1.);
b = sigma_td_nw((int) (s * maxwidth));
}
else {
s = we[i] / max(w0 - w1, 1.);
b = sigma_td_nw(maxwidth);
}
correct_table_matrix3(t, i, expand, s, b);
}
}
void
set_table_matrix(struct table *t, int width)
{
int size = t->maxcol + 1;
int i, j;
double b, s;
int a;
struct table_cell *cell = &t->cell;
if (size < 1)
return;
t->matrix = m_get(size, size);
t->vector = v_get(size);
for (i = 0; i < size; i++) {
for (j = i; j < size; j++)
m_set_val(t->matrix, i, j, 0.);
v_set_val(t->vector, i, 0.);
}
for (i = 0; i < size; i++) {
if (t->fixed_width[i] > 0) {
a = max(t->fixed_width[i], t->minimum_width[i]);
b = sigma_td(a);
correct_table_matrix(t, i, 1, a, b);
}
else if (t->fixed_width[i] < 0) {
s = -(double) t->fixed_width[i] / 100.;
b = sigma_td((int) (s * width));
correct_table_matrix2(t, i, 1, s, b);
}
}
for (j = 0; j <= cell->maxcell; j++) {
if (cell->fixed_width[j] > 0) {
a = max(cell->fixed_width[j], cell->minimum_width[j]);
b = sigma_td(a);
correct_table_matrix(t, cell->col[j],
cell->colspan[j], a, b);
}
else if (cell->fixed_width[j] < 0) {
s = -(double) cell->fixed_width[j] / 100.;
b = sigma_td((int) (s * width));
correct_table_matrix2(t, cell->col[j],
cell->colspan[j], s, b);
}
}
set_table_matrix0(t, width);
if (t->total_width > 0) {
b = sigma_table(width);
}
else {
b = sigma_table_nw(width);
}
correct_table_matrix(t, 0, size, width, b);
}
#endif /* MATRIX */
/* Local Variables: */
/* c-basic-offset: 4 */
/* tab-width: 8 */
/* End: */