diff options
author | Tatsuya Kinoshita <tats@vega.ocn.ne.jp> | 2011-05-04 07:18:09 +0000 |
---|---|---|
committer | Tatsuya Kinoshita <tats@vega.ocn.ne.jp> | 2011-05-04 07:18:09 +0000 |
commit | 5f8e0f8ef9a422691dd72e8a953a42a41478fcb4 (patch) | |
tree | 4b2df4796a534793648b3c4fc532fc36bd0cd525 /table.c | |
parent | Releasing debian version 0.3-2.4 (diff) | |
download | w3m-7867af0e1dc0477b9fdc08cf6e3aee68f03ae9f1.tar.gz w3m-7867af0e1dc0477b9fdc08cf6e3aee68f03ae9f1.zip |
Releasing debian version 0.5.1-1debian/0.5.1-1
Diffstat (limited to '')
-rw-r--r-- | table.c | 3617 |
1 files changed, 3617 insertions, 0 deletions
@@ -0,0 +1,3617 @@ +/* $Id: table.c,v 1.49 2004/01/09 15:46:49 ukai Exp $ */ +/* + * HTML table + */ +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <math.h> +#include "fm.h" +#include "html.h" +#include "parsetagx.h" +#include "Str.h" +#include "myctype.h" + +int symbol_width = 0; +int symbol_width0 = 0; + +#define RULE_WIDTH symbol_width +#define RULE(mode,n) (((mode) == BORDER_THICK) ? ((n) + 16) : (n)) +#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 */ + +#define set_prevchar(x,y,n) Strcopy_charp_n((x),(y),(n)) +#define set_space_to_prevchar(x) Strcopy_charp_n((x)," ",1) + +#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, + short *indexarray, int nent) +{ + int n = nent; + int k = 0; + + int e = e1 * base + e2; + while (n > 0) { + int nn = n / 2; + int idx = indexarray[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, short *indexarray, int nent) +{ + int n = nent; + int k = 0; + + while (n > 0) { + int nn = n / 2; + int idx = indexarray[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; + short *indexarray; + double *edv; + double w = 0., x; + + indexarray = NewAtom_N(short, 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, indexarray, k); + if (k > i) { + int ii; + for (ii = k; ii > i; ii--) + indexarray[ii] = indexarray[ii - 1]; + } + indexarray[i] = k; + } + iw = min((int)(w + 0.5), size); + if (iw == 0) + return; + x = edv[(int)indexarray[iw - 1]]; + for (i = 0; i < size; i++) { + k = indexarray[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; +#endif + t->linfo.prevchar = Strnew_size(8); + set_prevchar(t->linfo.prevchar, "", 0); + 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 ? 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 ? line : ""); + } +} + +#ifdef USE_M17N +#define PUSH_TAG(str,n) Strcat_charp_n(tagbuf, str, n) +#else +#define PUSH_TAG(str,n) Strcat_char(tagbuf, *str) +#endif + +int visible_length_offset = 0; +int +visible_length(char *str) +{ + int len = 0, n, max_len = 0; + int status = R_ST_NORMAL; + int prev_status = status; + Str tagbuf = Strnew(); + char *t, *r2; + int amp_len = 0; + + t = str; + while (*str) { + prev_status = status; + if (next_status(*str, &status)) { +#ifdef USE_M17N + len += get_mcwidth(str); + n = get_mclen(str); + } + else { + n = 1; + } +#else + len++; + } +#endif + if (status == R_ST_TAG0) { + Strclear(tagbuf); + PUSH_TAG(str, n); + } + else if (status == R_ST_TAG || status == R_ST_DQUOTE + || status == R_ST_QUOTE || status == R_ST_EQL + || status == R_ST_VALUE) { + PUSH_TAG(str, n); + } + else if (status == R_ST_AMP) { + if (prev_status == R_ST_NORMAL) { + Strclear(tagbuf); + len--; + amp_len = 0; + } + else { + PUSH_TAG(str, n); + amp_len++; + } + } + else if (status == R_ST_NORMAL && prev_status == R_ST_AMP) { + PUSH_TAG(str, n); + r2 = tagbuf->ptr; + t = getescapecmd(&r2); + if (!*r2 && (*t == '\r' || *t == '\n')) { + if (len > max_len) + max_len = len; + len = 0; + } + else + len += get_strwidth(t) + get_strwidth(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 == '\r' || *str == '\n') { + len--; + if (len > max_len) + max_len = len; + len = 0; + } +#ifdef USE_M17N + str += n; +#else + str++; +#endif + } + if (status == R_ST_AMP) { + r2 = tagbuf->ptr; + t = getescapecmd(&r2); + if (*t != '\r' && *t != '\n') + len += get_strwidth(t) + get_strwidth(r2); + } + return len > max_len ? len : max_len; +} + +int +visible_length_plain(char *str) +{ + int len = 0, max_len = 0; + + while (*str) { + if (*str == '\t') { + do { + len++; + } while ((visible_length_offset + len) % Tabstop != 0); + str++; + } + else if (*str == '\r' || *str == '\n') { + if (len > max_len) + max_len = len; + len = 0; + str++; + } + else { +#ifdef USE_M17N + len += get_mcwidth(str); + str += get_mclen(str); +#else + len++; + str++; +#endif + } + } + return len > max_len ? len : max_len; +} + +int +maximum_visible_length(char *str) +{ + int maxlen, len; + + visible_length_offset = 0; + maxlen = visible_length(str); + + if (!strchr(str, '\t')) + 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; +} + +int +maximum_visible_length_plain(char *str) +{ + int maxlen, len; + + visible_length_offset = 0; + maxlen = visible_length_plain(str); + + if (!strchr(str, '\t')) + return maxlen; + + for (visible_length_offset = 1; visible_length_offset < Tabstop; + visible_length_offset++) { + len = visible_length_plain(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; + int rule_mode; + int i, k, l, m; + + if (row >= 0) + check_row(t, row); + check_row(t, row + 1); + if ((type == T_TOP || type == T_BOTTOM) && t->border_mode == BORDER_THICK) { + rule_mode = BORDER_THICK; + } + else { + rule_mode = BORDER_THIN; + } + 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) { + push_symbol(buf, RULE(t->border_mode, forbid), symbol_width, 1); + } + 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 { + int w = t->tabwidth[i] + 2 * t->cellpadding; + if (RULE_WIDTH == 2) + w = (w + 1) / RULE_WIDTH; + push_symbol(buf, RULE(rule_mode, forbid), symbol_width, w); + } + 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' */ + push_symbol(buf, RULE(rule_mode, forbid), symbol_width, 1); + } + } + 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) + push_symbol(buf, RULE(t->border_mode, forbid), symbol_width, 1); +} + +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); + obuf.flag |= RB_INTABLE; + 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, 2, 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); + } + if (obuf.status != R_ST_NORMAL) { + obuf.status = R_ST_EOL; + HTMLlineproc1("\n", &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, + short *indexarray, int space, int dir) +{ + int i, j, k, bcol, ecol; + int swidth, width; + + for (k = 0; k <= maxcell; k++) { + j = indexarray[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; + short *indexarray; + char *fixed; + double *mod; + double sum = 0., x = 0.; + struct table_cell *cell = &t->cell; + int rulewidth = table_rule_width(t); + + indexarray = NewAtom_N(short, 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, indexarray, k); + if (k > i) { + int ii; + for (ii = k; ii > i; ii--) + indexarray[ii] = indexarray[ii - 1]; + } + indexarray[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 = indexarray[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 = indexarray[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 = indexarray[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 = indexarray[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 = indexarray[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->indexarray[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; + short *rowspan; + short *indexarray; + short maxcell; + short size; + short *height; + } cell; + int space = 0; + + cell.size = 0; + 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.indexarray, + c); + if (k <= cell.maxcell) { + int idx = cell.indexarray[k]; + if (cell.row[idx] == j && cell.rowspan[idx] == rowspan) + c = idx; + } + if (c >= MAXROWCELL) + continue; + if (c >= cell.size) { + if (cell.size == 0) { + cell.size = max(MAXCELL, c + 1); + cell.row = NewAtom_N(short, cell.size); + cell.rowspan = NewAtom_N(short, cell.size); + cell.indexarray = NewAtom_N(short, cell.size); + cell.height = NewAtom_N(short, cell.size); + } + else { + cell.size = max(cell.size + MAXCELL, c + 1); + cell.row = New_Reuse(short, cell.row, cell.size); + cell.rowspan = New_Reuse(short, cell.rowspan, + cell.size); + cell.indexarray = New_Reuse(short, cell.indexarray, + cell.size); + cell.height = New_Reuse(short, cell.height, cell.size); + } + } + 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) { + int ii; + for (ii = cell.maxcell; ii > k; ii--) + cell.indexarray[ii] = cell.indexarray[ii - 1]; + } + cell.indexarray[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.indexarray, 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 = NULL, vruleb = NULL, vrulec = NULL; +#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: + case BORDER_NOWIN: + 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(); + push_symbol(vrulea, TK_VERTICALBAR(t->border_mode), symbol_width, 1); + for (i = 0; i < t->cellpadding; i++) { + Strcat_char(vrulea, ' '); + Strcat_char(vruleb, ' '); + Strcat_char(vrulec, ' '); + } + push_symbol(vrulec, TK_VERTICALBAR(t->border_mode), symbol_width, 1); + case BORDER_NOWIN: + push_symbol(vruleb, TK_VERTICALBAR(BORDER_THIN), symbol_width, 1); + 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; + set_space_to_prevchar(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; + Str 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, *c = line; + int ec, len, wlen, plen; + ctype = get_mctype(line); + len = get_mcwidth(line); + wlen = plen = get_mclen(line); + + 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 = conv_entity(ec); + ctype = get_mctype(c); + len = get_strwidth(c); + wlen = line - save; + plen = get_mclen(c); + } + } + if (prevchar->length && is_boundary((unsigned char *)prevchar->ptr, + (unsigned char *)c)) { + w = len; + } + else { + w += len; + } + if (s > 0) { +#ifdef USE_M17N + if (ctype == PC_KANJI1 && prev_ctype == PC_KANJI1) + skip += s; + else +#endif + skip += s - 1; + } + s = 0; + prev_ctype = ctype; + } + set_prevchar(prevchar, c, plen); + 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; + mode->end_tag = 0; + 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; + mode->end_tag = 0; + 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 TAG_ACTION_PLAIN 4 + +#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; +#ifdef ID_EXT + char *p; +#endif + 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_PLAIN) { + if (mode->end_tag == cmd) { + mode->pre_mode &= ~TBLM_PLAIN; + mode->end_tag = 0; + feed_table_block_tag(tbl, line, mode, 0, cmd); + return TAG_ACTION_NONE; + } + return TAG_ACTION_PLAIN; + } + if (mode->pre_mode & TBLM_INTXTA) { + if (mode->end_tag == cmd) { + table_close_textarea(tbl, mode, width); + return TAG_ACTION_NONE; + } + return TAG_ACTION_FEED; + } + if (mode->pre_mode & TBLM_SCRIPT) { + if (mode->end_tag == cmd) { + mode->pre_mode &= ~TBLM_SCRIPT; + mode->end_tag = 0; + return TAG_ACTION_NONE; + } + return TAG_ACTION_PLAIN; + } + if (mode->pre_mode & TBLM_STYLE) { + if (mode->end_tag == cmd) { + mode->pre_mode &= ~TBLM_STYLE; + mode->end_tag = 0; + return TAG_ACTION_NONE; + } + return TAG_ACTION_PLAIN; + } + /* 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) { + switch (cmd) { + CASE_TABLE_TAG: + case HTML_N_FORM: + case HTML_N_SELECT: /* mode->end_tag */ + table_close_select(tbl, mode, width); + if (cmd == HTML_N_SELECT) + return TAG_ACTION_NONE; + break; + default: + return TAG_ACTION_FEED; + } + } + if (mode->caption) { + switch (cmd) { + CASE_TABLE_TAG: + case HTML_N_CAPTION: + mode->caption = 0; + if (cmd == HTML_N_CAPTION) + return TAG_ACTION_NONE; + break; + default: + 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) { + int ii; + for (ii = cell->maxcell; ii > k; ii--) + cell->index[ii] = cell->index[ii - 1]; + } + 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++) { +#if 0 + tbl->tabattr[tbl->row + i][tbl->col + j] &= ~(HTT_X | HTT_Y); +#endif + if (!(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_CENTER: + case HTML_N_CENTER: + case HTML_DIV: + case HTML_N_DIV: + if (!(tbl->flag & TBL_IN_ROW)) + break; + case HTML_DT: + case HTML_DD: + case HTML_H: + case HTML_N_H: + case HTML_LI: + case HTML_PRE: + case HTML_N_PRE: + case HTML_HR: + case HTML_LISTING: + case HTML_XMP: + case HTML_PLAINTEXT: + case HTML_PRE_PLAIN: + case HTML_N_PRE_PLAIN: + feed_table_block_tag(tbl, line, mode, 0, cmd); + switch (cmd) { + case HTML_PRE: + case HTML_PRE_PLAIN: + mode->pre_mode |= TBLM_PRE; + break; + case HTML_N_PRE: + case HTML_N_PRE_PLAIN: + mode->pre_mode &= ~TBLM_PRE; + break; + case HTML_LISTING: + mode->pre_mode |= TBLM_PLAIN; + mode->end_tag = HTML_N_LISTING; + break; + case HTML_XMP: + mode->pre_mode |= TBLM_PLAIN; + mode->end_tag = HTML_N_XMP; + break; + case HTML_PLAINTEXT: + mode->pre_mode |= TBLM_PLAIN; + mode->end_tag = MAX_HTMLTAG; + 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_NOBR: + case HTML_WBR: + if (!(tbl->flag & TBL_IN_ROW)) + break; + case HTML_PRE_INT: + 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: + if (!(tbl->flag & TBL_IN_ROW)) + break; + 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: + check_rowcol(tbl, mode); + w = tbl->fixed_width[tbl->col]; + if (w < 0) { + if (tbl->total_width > 0) + w = -tbl->total_width * w / 100; + else if (width > 0) + w = -width * w / 100; + else + w = 0; + } + else if (w == 0) { + if (tbl->total_width > 0) + w = tbl->total_width; + else if (width > 0) + w = width; + } + tok = process_img(tag, w); + feed_table1(tbl, tok, mode, width); + break; + case HTML_FORM: + feed_table_block_tag(tbl, "", mode, 0, cmd); + tmp = process_form(tag); + if (tmp) + feed_table1(tbl, tmp, mode, width); + break; + case HTML_N_FORM: + feed_table_block_tag(tbl, "", mode, 0, cmd); + process_n_form(); + break; + case HTML_INPUT: + tmp = process_input(tag); + feed_table1(tbl, tmp, mode, width); + break; + case HTML_SELECT: + tmp = process_select(tag); + if (tmp) + feed_table1(tbl, tmp, mode, width); + mode->pre_mode |= TBLM_INSELECT; + mode->end_tag = HTML_N_SELECT; + break; + case HTML_N_SELECT: + 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); + if (tmp) + feed_table1(tbl, tmp, mode, width); + mode->pre_mode |= TBLM_INTXTA; + mode->end_tag = HTML_N_TEXTAREA; + 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: + if (displayInsDel) + feed_table_inline_tag(tbl, line, mode, 5); /* [DEL: */ + else + mode->pre_mode |= TBLM_DEL; + break; + case HTML_N_DEL: + if (displayInsDel) + feed_table_inline_tag(tbl, line, mode, 5); /* :DEL] */ + else + mode->pre_mode &= ~TBLM_DEL; + break; + case HTML_S: + if (displayInsDel) + feed_table_inline_tag(tbl, line, mode, 3); /* [S: */ + else + mode->pre_mode |= TBLM_S; + break; + case HTML_N_S: + if (displayInsDel) + feed_table_inline_tag(tbl, line, mode, 3); /* :S] */ + else + mode->pre_mode &= ~TBLM_S; + break; + case HTML_INS: + case HTML_N_INS: + if (displayInsDel) + feed_table_inline_tag(tbl, line, mode, 5); /* [INS:, :INS] */ + break; + case HTML_SUP: + case HTML_SUB: + case HTML_N_SUB: + if (!(mode->pre_mode & (TBLM_DEL | TBLM_S))) + feed_table_inline_tag(tbl, line, mode, 1); /* ^, [, ] */ + break; + case HTML_N_SUP: + 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_N_CAPTION: + 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; + mode->end_tag = HTML_N_SCRIPT; + break; + case HTML_STYLE: + mode->pre_mode |= TBLM_STYLE; + mode->end_tag = HTML_N_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_INTERNAL: + case HTML_N_INTERNAL: + case HTML_FORM_INT: + case HTML_N_FORM_INT: + case HTML_INPUT_ALT: + case HTML_N_INPUT_ALT: + case HTML_SELECT_INT: + case HTML_N_SELECT_INT: + case HTML_OPTION_INT: + case HTML_TEXTAREA_INT: + case HTML_N_TEXTAREA_INT: + case HTML_IMG_ALT: + case HTML_SYMBOL: + case HTML_N_SYMBOL: + 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 == '<' && line[1] && REALLY_THE_BEGINNING_OF_A_TAG(line)) { + struct parsed_tag *tag; + p = line; + tag = parse_tag(&p, internal); + if (tag) { + switch (feed_table_tag(tbl, line, mode, width, tag)) { + case TAG_ACTION_NONE: + return -1; + case TAG_ACTION_N_TABLE: + return 0; + case TAG_ACTION_TABLE: + return 1; + case TAG_ACTION_PLAIN: + break; + case TAG_ACTION_FEED: + default: + if (parsedtag_need_reconstruct(tag)) + line = parsedtag2str(tag)->ptr; + } + } + else { + if (!(mode->pre_mode & (TBLM_PLAIN | TBLM_INTXTA | TBLM_INSELECT | + TBLM_SCRIPT | TBLM_STYLE))) + return -1; + } + } + else { + if (mode->pre_mode & (TBLM_DEL | TBLM_S)) + return -1; + } + if (mode->caption) { + Strcat_charp(tbl->caption, line); + return -1; + } + if (mode->pre_mode & TBLM_SCRIPT) + return -1; + if (mode->pre_mode & TBLM_STYLE) + 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 & ~TBLM_NOBR))) { + 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); + pushdata(tbl, tbl->row, tbl->col, line); + } + else if (mode->pre_mode & TBLM_PRE_INT) { + check_rowcol(tbl, mode); + if (mode->nobr_offset < 0) + mode->nobr_offset = tbl->tabcontentssize; + addcontentssize(tbl, maximum_visible_length(line)); + setwidth(tbl, mode); + pushdata(tbl, tbl->row, tbl->col, line); + } + else { + /* <pre> mode or something like it */ + check_rowcol(tbl, mode); + while (*line) { + int nl = FALSE; + if ((p = strchr(line, '\r')) || (p = strchr(line, '\n'))) { + if (*p == '\r' && p[1] == '\n') + p++; + if (p[1]) { + p++; + tmp = Strnew_charp_n(line, p - line); + line = p; + p = tmp->ptr; + } + else { + p = line; + line = ""; + } + nl = TRUE; + } + else { + p = line; + line = ""; + } + if (mode->pre_mode & TBLM_PLAIN) + i = maximum_visible_length_plain(p); + else + i = maximum_visible_length(p); + addcontentssize(tbl, i); + setwidth(tbl, mode); + if (nl) + clearcontentssize(tbl, mode); + pushdata(tbl, tbl->row, tbl->col, p); + } + } + 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) + bcopy(tbl->tables, tmp, tbl->ntable * sizeof(struct table_in)); + 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 +correct_table_matrix4(struct table *t, int col, int cspan, char *flags, + double s, double b) +{ + int i, j; + double ss; + int ecol = col + cspan; + int size = t->maxcol + 1; + double w = 1. / (b * b); + + for (i = 0; i < size; i++) { + if (flags[i] && !(i >= col && i < ecol)) + continue; + for (j = i; j < size; j++) { + if (flags[j] && !(j >= col && j < ecol)) + continue; + 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 +set_table_matrix0(struct table *t, int maxwidth) +{ + int size = t->maxcol + 1; + int i, j, k, bcol, ecol; + int width; + 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]; + width = cell->width[j] - (cell->colspan[j] - 1) * t->cellspacing; + w1 = 0.; + for (i = bcol; i < ecol; i++) { + w1 += t->tabwidth[i] + 0.1; + expand[i]++; + } + for (i = bcol; i < ecol; i++) { + w = weight(width * (t->tabwidth[i] + 0.1) / w1); + if (w > we[i]) + we[i] = w; + } + } + + w0 = 0.; + w1 = 0.; + for (i = 0; i < size; i++) { + w0 += we[i]; + if (expand[i] == 0) + w1 += we[i]; + } + if (w0 <= 0.) + w0 = 1.; + + for (k = 0; k < cell->necell; k++) { + j = cell->eindex[k]; + bcol = cell->col[j]; + width = cell->width[j] - (cell->colspan[j] - 1) * t->cellspacing; + w = weight(width); + s = w / (w1 + w); + b = sigma_td_nw((int)(s * maxwidth)); + correct_table_matrix4(t, bcol, cell->colspan[j], expand, s, b); + } + + 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 +check_relative_width(struct table *t, int maxwidth) +{ + int i; + double rel_total = 0; + int size = t->maxcol + 1; + double *rcolwidth = New_N(double, size); + struct table_cell *cell = &t->cell; + int n_leftcol = 0; + + for (i = 0; i < size; i++) + rcolwidth[i] = 0; + + for (i = 0; i < size; i++) { + if (t->fixed_width[i] < 0) + rcolwidth[i] = -(double)t->fixed_width[i] / 100.0; + else if (t->fixed_width[i] > 0) + rcolwidth[i] = (double)t->fixed_width[i] / maxwidth; + else + n_leftcol++; + } + for (i = 0; i <= cell->maxcell; i++) { + if (cell->fixed_width[i] < 0) { + double w = -(double)cell->fixed_width[i] / 100.0; + double r; + int j, k; + int n_leftcell = 0; + k = cell->col[i]; + r = 0.0; + for (j = 0; j < cell->colspan[i]; j++) { + if (rcolwidth[j + k] > 0) + r += rcolwidth[j + k]; + else + n_leftcell++; + } + if (n_leftcell == 0) { + /* w must be identical to r */ + if (w != r) + cell->fixed_width[i] = -100 * r; + } + else { + if (w <= r) { + /* make room for the left(width-unspecified) cell */ + /* the next formula is an estimation of required width */ + w = r * cell->colspan[i] / (cell->colspan[i] - n_leftcell); + cell->fixed_width[i] = -100 * w; + } + for (j = 0; j < cell->colspan[i]; j++) { + if (rcolwidth[j + k] == 0) + rcolwidth[j + k] = (w - r) / n_leftcell; + } + } + } + else if (cell->fixed_width[i] > 0) { + /* todo */ + } + } + /* sanity check */ + for (i = 0; i < size; i++) + rel_total += rcolwidth[i]; + + if ((n_leftcol == 0 && rel_total < 0.9) || 1.1 < rel_total) { + for (i = 0; i < size; i++) { + rcolwidth[i] /= rel_total; + } + for (i = 0; i < size; i++) { + if (t->fixed_width[i] < 0) + t->fixed_width[i] = -rcolwidth[i] * 100; + } + for (i = 0; i <= cell->maxcell; i++) { + if (cell->fixed_width[i] < 0) { + double r; + int j, k; + k = cell->col[i]; + r = 0.0; + for (j = 0; j < cell->colspan[i]; j++) + r += rcolwidth[j + k]; + cell->fixed_width[i] = -r * 100; + } + } + } +} + +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.); + } + + check_relative_width(t, width); + + 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: */ |