diff options
Diffstat (limited to '')
-rw-r--r-- | table.c | 3328 | ||||
-rw-r--r-- | table.c.0 | 2878 |
2 files changed, 6206 insertions, 0 deletions
@@ -0,0 +1,3328 @@ +/* $Id: table.c,v 1.1 2001/11/08 05:15:40 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 */ +#if defined(__EMX__)&&!defined(JP_CHARSET) +extern int CodePage; + +static char *_rule[] = +#else +char alt_rule[] = { +'+', '|', '-', '+', '|', '|', '+', ' ', '-', '+', '-', ' ', '+', ' ', ' ', ' '}; +static char *rule[] = +#endif +{ + "<_RULE>\200</_RULE>", + "<_RULE>\201</_RULE>", + "<_RULE>\202</_RULE>", + "<_RULE>\203</_RULE>", + "<_RULE>\204</_RULE>", + "<_RULE>\205</_RULE>", + "<_RULE>\206</_RULE>", + "<_RULE>\207</_RULE>", + "<_RULE>\210</_RULE>", + "<_RULE>\211</_RULE>", + "<_RULE>\212</_RULE>", + "<_RULE>\213</_RULE>", + "<_RULE>\214</_RULE>", + "<_RULE>\215</_RULE>", + "<_RULE>\216</_RULE>", + "<_RULE>\217</_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>\205</_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 = New_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; + 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); + } + 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; +} + +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 > cell.maxcell && c < 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 (c <= cell.maxcell && c >= 0 && + 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]; + TextLineList *tl; + Str tmp; + + if (t->caption->length <= 0) + return; + + if (t->total_width <= 0) + t->total_width = h_env->limit; + + init_henv(&henv, &obuf, envs, MAX_ENV_LEVEL, newTextLineList(), + t->total_width, h_env->envs[h_env->envc].indent); + HTMLlineproc1("<center>", &henv); + HTMLlineproc0(t->caption->ptr, &henv, FALSE); + HTMLlineproc1("</center>", &henv); + + tl = henv.buf; + + if (tl->nitem > 0) { + TextLineListItem *ti; + tmp = Strnew_charp("<pre for_table>"); + for (ti = tl->first; ti != NULL; ti = ti->next) { + Strcat(tmp, ti->ptr->line); + Strcat_char(tmp, '\n'); + } + Strcat_charp(tmp, "</pre>"); + HTMLlineproc1(tmp->ptr, h_env); + } +} + +void +renderTable(struct table *t, + int max_width, + struct html_feed_environ *h_env) +{ + int i, j, w, r, h; + Str renderbuf = Strnew(); + short new_tabwidth[MAXCOL]; +#ifdef MATRIX + int itr; + VEC *newwidth; + MAT *mat, *minv; + PERM *pivot; +#endif /* MATRIX */ + 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 */ + make_caption(t, h_env); + + HTMLlineproc1("<pre for_table>", h_env); +#ifdef ID_EXT + if (t->id != NULL) { + idtag = Sprintf("<_id id=\"%s\">", htmlquote_str((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, 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\">", + htmlquote_str((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\">", + htmlquote_str((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, 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, 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, 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, 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) { + c = ec; + ctype = IS_CNTRL(ec) ? PC_CTRL : PC_ASCII; + len = strlen(conv_latin1(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; + + 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; + } + } + + if (mode->pre_mode & TBLM_IGNORE) { + switch (cmd) { + case HTML_N_STYLE: + mode->pre_mode &= ~TBLM_STYLE; + case HTML_N_SCRIPT: + mode->pre_mode &= ~TBLM_SCRIPT; + default: + return TAG_ACTION_NONE; + } + } + + /* 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: + 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; + } + 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; +#ifdef NEW_FORM + case HTML_FORM: + process_form(tag); + break; + case HTML_N_FORM: + process_n_form(); + break; +#else /* not NEW_FORM */ + case HTML_FORM: + case HTML_N_FORM: + feed_table_block_tag(tbl, "<br>", mode, 0, HTML_BR); + if (line[1] == '/') { + tok = Strnew_charp("</form_int "); + Strcat_charp(tok, line + 6); + } + else { + tok = Strnew_charp("<form_int "); + Strcat_charp(tok, line + 5); + } + pushdata(tbl, tbl->row, tbl->col, tok->ptr); + break; +#endif /* not NEW_FORM */ + 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; + ec = getescapechar(&p); + r = conv_latin1(ec); + if (r != NULL && strlen(r) == 1 && + ec == (unsigned char)*r) { + Strcat_char(tmp, *r); + } + else { + Strcat_char(tmp, *q); + p = q + 1; + } + } + } + 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: */ diff --git a/table.c.0 b/table.c.0 new file mode 100644 index 0000000..f3cacb1 --- /dev/null +++ b/table.c.0 @@ -0,0 +1,2878 @@ +/* -*- mode: c; c-basic-offset: 4; tab-width: 8; -*- */ +/* $Id: table.c.0,v 1.1 2001/11/08 05:15:52 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 "parsetag.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 */ +static char *rule[] = +{ + "<_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>07</_RULE>", + "<_RULE TYPE=8>+</_RULE>", + "<_RULE TYPE=9>+</_RULE>", + "<_RULE TYPE=10>-</_RULE>", + "<_RULE TYPE=11>0B</_RULE>", + "<_RULE TYPE=12>+</_RULE>", + "<_RULE TYPE=13>0D</_RULE>", + "<_RULE TYPE=14>0E</_RULE>", + "<_RULE TYPE=15> </_RULE>"}; +static char **ruleB = rule; +#define TN_VERTICALBAR "<_RULE TYPE=5>|</_RULE>" +#define HORIZONTALBAR "<_RULE TYPE=10>-</_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 +#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 +dsort_index(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 +fsort_index(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 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 = fsort_index(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]--; + } +} + +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(TextList **, 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->tabcontentssize = 0; + t->indent = 0; + t->ntable = 0; + t->tables_size = 0; + t->tables = NULL; +#ifdef MATRIX + t->matrix = NULL; + t->vector = NULL; +#endif /* MATRIX */ + t->linfo.prev_ctype = PC_ASCII; + t->linfo.prev_spaces = -1; + t->linfo.prevchar = ' '; + t->trattr = 0; + + t->status = R_ST_NORMAL; + t->suspended_input = Strnew(); + t->caption = Strnew(); +#ifdef ID_EXT + t->id = NULL; +#endif + return t; +} + +static void +check_row(struct table *t, int row) +{ + int i, r; + TextList ***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(TextList **, r); + tabattr = New_N(table_attr *, r); + tabheight = New_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(TextList *, 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] = newTextList(); + + pushText(t->tabdata[row][col], data); +} + +int visible_length_offset = 0; +int +visible_length(char *str) +{ + int len = 0; + int status = R_ST_NORMAL; + int prev_status = status; + Str tagbuf = Strnew(); + char *t, *r2; + struct parsed_tagarg *t_arg, *tt; + 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)) { + Strcat_char(tagbuf, *str); + if (TAG_IS(tagbuf->ptr, "<img", 4)) { + int anchor_len = 0; + t_arg = parse_tag(tagbuf->ptr + 5); + for (tt = t_arg; tt; tt = tt->next) { + if (strcasecmp(tt->arg, "src") == 0 && tt->value && anchor_len == 0) { + r2 = tt->value + strlen(tt->value) - 1; + while (tt->value < r2 && *r2 != '/') + r2--; + if (*r2 == '/') + r2++; + while (*r2 && *r2 != '.') { + r2++; + anchor_len++; + } + anchor_len += 2; + } + else if (strcasecmp(tt->arg, "alt") == 0 && tt->value) { + anchor_len = strlen(tt->value) + 1; + break; + } + } + len += anchor_len; + } + else if (TAG_IS(tagbuf->ptr, "<input", 6)) { + int width = 20; + int valuelen = 1; + int input_type = FORM_INPUT_TEXT; + t_arg = parse_tag(tagbuf->ptr + 7); + for (tt = t_arg; tt; tt = tt->next) { + if (strcasecmp(tt->arg, "type") == 0 && tt->value) { + input_type = formtype(tt->value); + } + else if (strcasecmp(tt->arg, "value") == 0 && tt->value) { + valuelen = strlen(tt->value); + } + else if (strcasecmp(tt->arg, "width") == 0 && tt->value) { + width = atoi(tt->value); + } + } + switch (input_type) { + case FORM_INPUT_TEXT: + case FORM_INPUT_FILE: + case FORM_INPUT_PASSWORD: + len += width + 2; + break; + case FORM_INPUT_SUBMIT: + case FORM_INPUT_RESET: + case FORM_INPUT_IMAGE: + case FORM_INPUT_BUTTON: + len += valuelen + 2; + break; + case FORM_INPUT_CHECKBOX: + case FORM_INPUT_RADIO: + len += 3; + } + } + else if (TAG_IS(tagbuf->ptr, "<textarea", 9)) { + int width = 20; + t_arg = parse_tag(tagbuf->ptr + 7); + for (tt = t_arg; tt; tt = tt->next) { + if (strcasecmp(tt->arg, "cols") == 0 && tt->value) { + width = atoi(tt->value); + } + } + len += width + 2; + } + else if (TAG_IS(tagbuf->ptr, "<option", 7)) + len += 3; + } + else if (*str == '\t') { + len--; + do { + len++; + } while ((visible_length_offset + len) % Tabstop != 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; +} + +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; +} + +char * +align(char *str, int width, int mode) +{ + int i, l, l1, l2; + Str buf = Strnew(); + + if (str == NULL || *str == '\0') { + for (i = 0; i < width; i++) + Strcat_char(buf, ' '); + return buf->ptr; + } + l = width - visible_length(str); + switch (mode) { + case ALIGN_CENTER: + l1 = l / 2; + l2 = l - l1; + for (i = 0; i < l1; i++) + Strcat_char(buf, ' '); + Strcat_charp(buf, str); + for (i = 0; i < l2; i++) + Strcat_char(buf, ' '); + return buf->ptr; + case ALIGN_LEFT: + Strcat_charp(buf, str); + for (i = 0; i < l; i++) + Strcat_char(buf, ' '); + return buf->ptr; + case ALIGN_RIGHT: + for (i = 0; i < l; i++) + Strcat_char(buf, ' '); + Strcat_charp(buf, str); + return buf->ptr; + } + return Strnew_charp(str)->ptr; +} + +void +print_item(struct table *t, + int row, int col, int width, + Str buf) +{ + int alignment; + char *p; + + if (t->tabdata[row]) + p = popText(t->tabdata[row][col]); + else + p = NULL; + + if (p != 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; + Strcat_charp(buf, align(p, width, alignment)); + } + else + Strcat_charp(buf, align(NULL, width, ALIGN_CENTER)); +} + + +#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]); + Strcat_charp(buf, "<eol>"); +} + +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) +{ + TextList *orgdata; + TextListItem *l; + struct readbuffer obuf; + struct html_feed_environ h_env; + struct environment envs[MAX_ENV_LEVEL]; + + if (tbl->tabdata[row] == NULL || + tbl->tabdata[row][col] == NULL) + return; + orgdata = tbl->tabdata[row][col]; + tbl->tabdata[row][col] = newTextList(); + + init_henv(&h_env, &obuf, envs, MAX_ENV_LEVEL, tbl->tabdata[row][col], + tbl->tabwidth[col], 0); + h_env.limit = get_spec_cell_width(tbl, row, col); + for (l = orgdata->first; l != NULL; l = l->next) { + if (TAG_IS(l->ptr, "<dummy_table", 12)) { + struct parsed_tagarg *t_arg, *t; + int id = -1; + t_arg = parse_tag(l->ptr + 12); + for (t = t_arg; t; t = t->next) { + if (!strcasecmp(t->arg, "id") && t->value) { + id = atoi(t->value); + break; + } + } + if (id >= 0) { + int alignment; + TextListItem *ti; + save_fonteffect(&h_env, h_env.obuf); + flushline(&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) { + appendTextList(h_env.buf, tbl->tables[id].buf); + } + else { + for (ti = tbl->tables[id].buf->first; ti != NULL; ti = ti->next) { + pushText(h_env.buf, align(ti->ptr, h_env.limit, alignment)); + } + } + restore_fonteffect(&h_env, h_env.obuf); + } + } + else + HTMLlineproc1(l->ptr, &h_env); + } + flushline(&h_env, &obuf, 0, 0, h_env.limit); +} + +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 int +recalc_width(int old, int delta, double rat) +{ + double w = rat * old; + double ww = (double) delta; + if (w > 0.) { + if (ww < 0.) + ww = 0.; + ww += 0.2; + } + else { + if (ww > 0.) + ww = 0.; + ww -= 1.0; + } + if (w > ww) + return (int) (ww / rat); + return old; +} + +static int +check_compressible_cell(struct table *t, MAT * minv, + short *newwidth, short *swidth, short *cwidth, + int totalwidth, + int icol, int icell, double sxx, int corr) +{ + struct table_cell *cell = &t->cell; + int i, j, k, m, bcol, ecol; + int delta, dmax, dmin, owidth; + double sxy; + + if (sxx < 10.) + return corr; + + if (icol >= 0) { + owidth = newwidth[icol]; + delta = newwidth[icol] - t->tabwidth[icol]; + bcol = icol; + ecol = bcol + 1; + } + else if (icell >= 0) { + owidth = swidth[icell]; + delta = swidth[icell] - 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 = 0; + for (k = 0; k <= cell->maxcell; k++) { + int bcol1, ecol1; + if (dmin <= 0) + return corr; + j = cell->index[k]; + if (icol < 0 && 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 (fabs(delta * sxy / sxx) < 0.5) + continue; + if (sxy > 0.) + dmin = recalc_width(dmin, swidth[j] - cwidth[j], sxy / sxx); + else + dmax = recalc_width(dmax, swidth[j] - cwidth[j], sxy / sxx); + } + for (m = 0; m <= t->maxcol; m++) { + if (dmin <= 0) + return corr; + if (icol >= 0 && m == icol) + continue; + sxy = 0.; + for (i = bcol; i < ecol; i++) + sxy += m_entry(minv, i, m); + if (fabs(delta * sxy / sxx) < 0.5) + continue; + if (sxy > 0.) + dmin = recalc_width(dmin, newwidth[m] - t->tabwidth[m], sxy / sxx); + else + dmax = recalc_width(dmax, newwidth[m] - t->tabwidth[m], sxy / sxx); + } + if (dmax > 0 && dmin > dmax) + dmin = dmax; + if (dmin > 1) { + correct_table_matrix(t, bcol, ecol - bcol, owidth - dmin, 1.); + corr++; + } + return corr; +} + +#define MAX_ITERATION 3 +int +check_table_width(struct table *t, short *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]; + short cwidth[cell->maxcell + 1], swidth[cell->maxcell + 1]; +#else /* __GNUC__ */ + short orgwidth[MAXCOL]; + short cwidth[MAXCELL], swidth[MAXCELL]; +#endif /* __GNUC__ */ + int twidth; + double 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, + -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, + -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, + i, -1, m_entry(minv, i, i), corr); + if (itr < MAX_ITERATION && corr > 0) + return corr; + } + + + for (i = 0; i <= t->maxcol; i++) + orgwidth[i] = newwidth[i]; + + check_minimum_width(t, newwidth); + + 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] && + newwidth[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 += newwidth[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 = dsort_index(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 > cell.maxcell && c < 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 (c <= cell.maxcell && c >= 0 && + 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); +} + +int +get_table_width(struct table *t, short *orgwidth, short *cellwidth, + int check_minimum) +{ +#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; + + for (i = 0; i <= t->maxcol; i++) + newwidth[i] = max(orgwidth[i], 0); + + check_cell_width(newwidth, cellwidth, cell->col, cell->colspan, + cell->maxcell, cell->index, t->cellspacing, 0); + if (check_minimum) + check_minimum_width(t, newwidth); + + swidth = 0; + for (i = 0; i <= t->maxcol; i++) { + swidth += newwidth[i]; + } + 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,0)) +#define fixed_table_width(t)\ + (get_table_width(t,t->fixed_width,t->cell.fixed_width,1)) + +void +renderCoTable(struct table *tbl) +{ + struct readbuffer obuf; + struct html_feed_environ h_env; + struct environment envs[MAX_ENV_LEVEL]; + struct table *t; + int i, j, col, row, b_width; + int width, nwidth, indent, maxwidth; +#ifdef __GNUC__ + short newwidth[tbl->maxcol + 1]; +#else /* not __GNUC__ */ + short newwidth[MAXCOL]; +#endif /* not __GNUC__ */ + + for (i = 0; i <= tbl->maxcol; i++) + newwidth[i] = tbl->tabwidth[i]; + + 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, + tbl->tabwidth[col], indent); + nwidth = newwidth[col]; + check_row(tbl, row); + b_width = 0; + for (j = col + 1; j <= tbl->maxcol; j++) { + if (tbl->tabattr[row][j] & HTT_X) { + h_env.limit += tbl->tabwidth[j]; + nwidth += newwidth[j]; + b_width += tbl->cellspacing; + } + else + break; + } + h_env.limit += b_width; +#ifndef TABLE_EXPAND + 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 * + get_spec_cell_width(tbl, row, col) / 100; +#else + maxwidth = h_env.limit - indent; + if (t->total_width > 0) { + double r = ((double) maxwidth) / t->total_width; + struct table_cell *cell = &t->cell; + + for (j = 0; j <= t->maxcol; j++) { + if (t->fixed_width[j] > 0) + t->fixed_width[j] = (int) (r * t->fixed_width[j]); + } + for (j = 0; j <= cell->maxcell; j++) { + if (cell->fixed_width[j] > 0) + cell->fixed_width[j] = (int) (r * cell->fixed_width[j]); + } + t->total_width = maxwidth; + } +#endif /* TABLE_EXPAND */ + renderTable(t, maxwidth, &h_env); + width = t->total_width - b_width + indent; + if (width > nwidth) { + int cell = tbl->tables[i].cell; + if (cell < 0) { + newwidth[col] = width; + } + else { + int ecol = col + tbl->cell.colspan[cell]; + int w = (width - nwidth) / tbl->cell.colspan[cell]; + int r = (width - nwidth) % tbl->cell.colspan[cell]; + for (j = col; j < ecol; j++) + newwidth[j] += w; + for (j = 1; j <= r; j++) + newwidth[ecol - j]++; + } + } + t = NULL; + } + for (i = 0; i <= tbl->maxcol; i++) + tbl->tabwidth[i] = newwidth[i]; +} + +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]; + TextList *tl; + Str tmp; + + if (t->caption->length <= 0) + return; + + if (t->total_width <= 0) + t->total_width = h_env->limit; + + init_henv(&henv, &obuf, envs, MAX_ENV_LEVEL, newTextList(), t->total_width, + h_env->envs[h_env->envc].indent); + HTMLlineproc1("<center>", &henv); + HTMLlineproc1(t->caption->ptr, &henv); + HTMLlineproc1("</center>", &henv); + + tl = henv.buf; + + if (tl->nitem > 0) { + TextListItem *ti; + tmp = Strnew_charp("<pre for_table>"); + for (ti = tl->first; ti != NULL; ti = ti->next) { + Strcat_charp(tmp, ti->ptr); + Strcat_char(tmp, '\n'); + } + Strcat_charp(tmp, "</pre>"); + HTMLlineproc1(tmp->ptr, h_env); + } +} + +void +renderTable(struct table *t, + int max_width, + struct html_feed_environ *h_env) +{ + int i, j, w, r, h; + Str renderbuf = Strnew(); + short new_tabwidth[MAXCOL]; +#ifdef MATRIX + int itr; + VEC *newwidth; + MAT *mat, *minv; + PERM *pivot; +#endif /* MATRIX */ + int maxheight = 0; + Str vrulea, vruleb, vrulec; +#ifdef ID_EXT + Str idtag; +#endif /* ID_EXT */ + + if (t->maxcol < 0) { + make_caption(t, h_env); + return; + } + + max_width -= table_border_width(t); + + if (max_width <= 0) + max_width = 1; + + 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); + dv2sv(newwidth->ve, new_tabwidth, t->maxcol + 1); + LUinverse(mat, pivot, minv); +#ifdef TABLE_DEBUG + 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, "\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, new_tabwidth, minv, itr)); + 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 */ + + renderCoTable(t); + check_minimum_width(t, t->tabwidth); + + t->total_width = 0; + for (i = 0; i <= t->maxcol; i++) { + if (t->border_mode != BORDER_NONE && t->tabwidth[i] % RULE_WIDTH == 1) + t->tabwidth[i]++; + t->total_width += t->tabwidth[i]; + } + + t->total_width += table_border_width(t); + + 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); + } + } + + check_table_height(t); + + /* table output */ + make_caption(t, h_env); + + HTMLlineproc1("<pre for_table>", h_env); +#ifdef ID_EXT + if (t->id != NULL) { + idtag = Sprintf("<_id id=\"%s\">", (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); + HTMLlineproc1(renderbuf->ptr, h_env); + maxheight += 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?"³":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\">", (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\">", (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); + Strcat_charp(renderbuf, "<eol>"); + maxheight += 1; + break; + case BORDER_NONE: + case BORDER_NOWIN: + Strcat_charp(renderbuf, "<eol>"); + break; + } + HTMLlineproc1(renderbuf->ptr, h_env); + } + if (r < t->maxrow && t->border_mode != BORDER_NONE) { + renderbuf = Strnew(); + print_sep(t, r, T_MIDDLE, t->maxcol, renderbuf); + HTMLlineproc1(renderbuf->ptr, h_env); + } + maxheight += t->tabheight[r]; + } + if (t->border_mode == BORDER_THIN || t->border_mode == BORDER_THICK) { + renderbuf = Strnew(); + print_sep(t, t->maxrow, T_BOTTOM, t->maxcol, renderbuf); + HTMLlineproc1(renderbuf->ptr, h_env); + maxheight += 1; + } + if (maxheight == 0) + HTMLlineproc1(" <eol>", h_env); + HTMLlineproc1("</pre>", h_env); +} + + +struct table * +begin_table(int border, int spacing, int padding) +{ + struct table *t; + int mincell = minimum_cellspacing(border); + + 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; + t->cellspacing = max(spacing, mincell); + switch (border) { + case BORDER_THIN: + case BORDER_THICK: + case BORDER_NOWIN: + t->cellpadding = (t->cellspacing - mincell) / 2; + break; + case BORDER_NONE: + t->cellpadding = t->cellspacing - mincell; + } + + if (padding > t->cellpadding) + t->cellpadding = padding; + + 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; + } + return t; +} + +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 +clearcontentssize(struct table *t, struct table_mode *mode) +{ + mode->nobr_offset = 0; + t->linfo.prev_spaces = -1; + t->linfo.prevchar = ' '; + t->tabcontentssize = 0; +} + +void +check_rowcol(struct table *tbl) +{ + 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; + + 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 = linfo->prev_ctype, prev_ctype; + char prevchar = linfo->prevchar; + int w = (linfo->prev_spaces == -1) ? 0 : linfo->length; + int min = 1; + + if (*line == '<' && line[strlen(line) - 1] == '>') { + if (checkminimum) + check_minimum0(t, visible_length(line)); + return 0; + } + + while (*line) { + char c = *line, *save = line; + int ec = '\0', len = 1, wlen = 1; + prev_ctype = ctype; + ctype = get_ctype(c, prev_ctype); + if (min < w) + min = w; + if (ctype == PC_ASCII && IS_SPACE(c)) { + w = 0; + s++; + } + else { + if (c == '&') { + ec = getescapechar(&line); + if (ec) { + c = ec; + if (IS_CNTRL(ec)) + ctype = PC_CTRL; + else + ctype = PC_ASCII; + len = strlen(conv_latin1(ec)); + wlen = line - save; + } + } + if (prevchar && is_boundary(prevchar, prev_ctype, c, ctype)) { + w = len; + } + else { + w += len; + } + if (s > 0) { +#ifdef JP_CHARSET + if (ctype == PC_KANJI1 && prev_ctype == PC_KANJI2) + skip += s; + else +#endif /* JP_CHARSET */ + skip += s - 1; + } + s = 0; + } + prevchar = c; + line = save + wlen; + } + if (s > 1) { + skip += s - 1; + linfo->prev_spaces = 1; + } + else { + linfo->prev_spaces = s; + } + linfo->prev_ctype = ctype; + linfo->prevchar = prevchar; + + if (checkminimum) { + if (min < w) + min = w; + linfo->length = w; + check_minimum0(t, min); + } + return skip; +} + +#define TAG_ACTION_NONE 0 +#define TAG_ACTION_FEED 1 +#define TAG_ACTION_TABLE 2 +#define TAG_ACTION_N_TABLE 3 + +static int +feed_table_tag(struct table *tbl, char *line, struct table_mode *mode, int width) +{ + int cmd; + char *s_line; + struct table_cell *cell = &tbl->cell; + struct parsed_tagarg *t_arg, *t; + int colspan, rowspan; + int col, prev_col; + int i, j, k, v, v0, w, id, status; + Str tok, tmp, anchor; + table_attr align; + + s_line = line; + cmd = gethtmlcmd(&s_line, &status); + + if (mode->pre_mode & TBLM_IGNORE) { + switch (cmd) { + case HTML_N_STYLE: + mode->pre_mode &= ~TBLM_STYLE; + case HTML_N_SCRIPT: + mode->pre_mode &= ~TBLM_SCRIPT; + default: + return TAG_ACTION_NONE; + } + } + +#ifdef MENU_SELECT + /* 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 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: + mode->pre_mode &= ~TBLM_INSELECT; + tmp = process_n_select(); + feed_table1(tbl, tmp, mode, width); + } + } +#endif /* MENU_SELECT */ + + switch (cmd) { + case HTML_TABLE: + return TAG_ACTION_TABLE; + case HTML_N_TABLE: + return TAG_ACTION_N_TABLE; + case HTML_TR: + if (tbl->col >= 0 && tbl->tabcontentssize > 0) + setwidth(tbl, mode); + clearcontentssize(tbl, mode); + mode->caption = 0; + mode->indent_level = 0; + mode->nobr_level = 0; + mode->mode_level = 0; + mode->pre_mode = 0; + tbl->col = -1; + tbl->row++; + tbl->flag |= TBL_IN_ROW; + tbl->indent = 0; + align = 0; + t_arg = parse_tag(line + 3); + for (t = t_arg; t; t = t->next) { + if (!strcasecmp(t->arg, "align") && t->value) { + if (!strcasecmp(t->value, "left")) + align = (HTT_LEFT | HTT_TRSET); + else if (!strcasecmp(t->value, "right")) + align = (HTT_RIGHT | HTT_TRSET); + else if (!strcasecmp(t->value, "center")) + align = (HTT_CENTER | HTT_TRSET); + } +#ifdef ID_EXT + if (!strcasecmp(t->arg, "id") && t->value) { + tbl->tridvalue[tbl->row] = Strnew_charp(t->value); + } +#endif /* ID_EXT */ + } + tbl->trattr = align; + break; + case HTML_TH: + case HTML_TD: + prev_col = tbl->col; + if (tbl->col >= 0 && tbl->tabcontentssize > 0) + setwidth(tbl, mode); + clearcontentssize(tbl, mode); + mode->caption = 0; + mode->indent_level = 0; + mode->nobr_level = 0; + mode->mode_level = 0; + mode->pre_mode = 0; + tbl->flag |= TBL_IN_COL; + tbl->linfo.prev_spaces = -1; + tbl->linfo.prevchar = ' '; + tbl->linfo.prev_ctype = PC_ASCII; + tbl->indent = 0; + 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; + } + tbl->height = 0; + t_arg = parse_tag(line + 3); + colspan = rowspan = 1; + v = 0; + if (tbl->trattr & HTT_TRSET) + align = (tbl->trattr & HTT_ALIGN); + else if (cmd == HTML_TH) + align = HTT_CENTER; + else + align = HTT_LEFT; + for (t = t_arg; t; t = t->next) { + if (!strcasecmp(t->arg, "rowspan") && t->value) { + rowspan = atoi(t->value); + if ((tbl->row + rowspan) >= tbl->max_rowsize) + check_row(tbl, tbl->row + rowspan); + } + else if (!strcasecmp(t->arg, "colspan") && t->value) { + colspan = atoi(t->value); + if ((tbl->col + colspan) >= MAXCOL) { + /* Can't expand column */ + colspan = MAXCOL - tbl->col; + } + } + else if (!strcasecmp(t->arg, "align") && t->value) { + if (!strcasecmp(t->value, "left")) + align = HTT_LEFT; + else if (!strcasecmp(t->value, "right")) + align = HTT_RIGHT; + else if (!strcasecmp(t->value, "center")) + align = HTT_CENTER; + } +#ifdef NOWRAP + else if (!strcasecmp(t->arg, "nowrap")) + tbl->tabattr[tbl->row][tbl->col] |= HTT_NOWRAP; +#endif /* NOWRAP */ + else if (!strcasecmp(t->arg, "width") && t->value) { + if (IS_DIGIT(*t->value)) { + v = atoi(t->value); + if (v == 0) + v = 1; + else if (v < 0) + v = 0; + if (t->value[strlen(t->value) - 1] == '%') { + v = -v; + } + else { +#ifdef TABLE_EXPAND + v = max(v / tbl->ppc, 1); +#else /* not TABLE_EXPAND */ + v = v / PIXEL_PER_CHAR; +#endif /* not TABLE_EXPAND */ + } + } + else + continue; +#ifdef ID_EXT + } + else if (!strcasecmp(t->arg, "id") && t->value) { + tbl->tabidvalue[tbl->row][tbl->col] = Strnew_charp(t->value); +#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; + tbl->tabattr[tbl->row][tbl->col] |= align; + if (colspan > 1) { + col = tbl->col; + + cell->icell = cell->maxcell + 1; + k = dsort_index(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 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; + } + } + 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 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 = 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: + check_rowcol(tbl); + pushdata(tbl, tbl->row, tbl->col, line); + setwidth(tbl, mode); + clearcontentssize(tbl, mode); + if (cmd == HTML_DD) + addcontentssize(tbl, tbl->indent); + break; + case HTML_DL: + case HTML_BLQ: + case HTML_OL: + case HTML_UL: + check_rowcol(tbl); + pushdata(tbl, tbl->row, tbl->col, line); + setwidth(tbl, mode); + clearcontentssize(tbl, mode); + mode->indent_level++; + if (mode->indent_level <= MAX_INDENT_LEVEL) + tbl->indent += INDENT_INCR; + if (cmd == HTML_BLQ) { + check_minimum0(tbl, 0); + addcontentssize(tbl, tbl->indent); + } + break; + case HTML_N_DL: + case HTML_N_BLQ: + case HTML_N_OL: + case HTML_N_UL: + if (mode->indent_level > 0) { + check_rowcol(tbl); + pushdata(tbl, tbl->row, tbl->col, line); + setwidth(tbl, mode); + clearcontentssize(tbl, mode); + mode->indent_level--; + if (mode->indent_level < MAX_INDENT_LEVEL) + tbl->indent -= INDENT_INCR; + } + break; + case HTML_LI: + check_rowcol(tbl); + pushdata(tbl, tbl->row, tbl->col, line); + setwidth(tbl, mode); + clearcontentssize(tbl, mode); + check_minimum0(tbl, 0); + addcontentssize(tbl, tbl->indent); + break; + case HTML_PRE: + case HTML_LISTING: + case HTML_XMP: + setwidth(tbl, mode); + check_rowcol(tbl); + clearcontentssize(tbl, mode); + pushdata(tbl, tbl->row, tbl->col, line); + mode->pre_mode |= TBLM_PRE; + mode->mode_level++; + break; + case HTML_N_PRE: + case HTML_N_LISTING: + case HTML_N_XMP: + setwidth(tbl, mode); + check_rowcol(tbl); + clearcontentssize(tbl, mode); + pushdata(tbl, tbl->row, tbl->col, line); + if (mode->mode_level > 0) + mode->mode_level--; + if (mode->mode_level == 0) + mode->pre_mode &= ~TBLM_PRE; + tbl->linfo.prev_spaces = 0; + tbl->linfo.prevchar = '\0'; + tbl->linfo.prev_ctype = PC_ASCII; + break; + case HTML_NOBR: + check_rowcol(tbl); + pushdata(tbl, tbl->row, tbl->col, line); + if (!(mode->pre_mode & TBLM_NOBR)) { + mode->nobr_offset = -1; + mode->pre_mode |= TBLM_NOBR; + } + mode->nobr_level++; + break; + case HTML_WBR: + check_rowcol(tbl); + pushdata(tbl, tbl->row, tbl->col, line); + mode->nobr_offset = -1; + break; + case HTML_N_NOBR: + if (mode->nobr_level > 0) + mode->nobr_level--; + check_rowcol(tbl); + pushdata(tbl, tbl->row, tbl->col, line); + if (mode->nobr_level == 0) + mode->pre_mode &= ~TBLM_NOBR; + break; + case HTML_PRE_INT: + check_rowcol(tbl); + pushdata(tbl, tbl->row, tbl->col, line); + if (!(mode->pre_mode & TBLM_PRE_INT)) { + mode->nobr_offset = -1; + mode->pre_mode |= TBLM_PRE_INT; + } + tbl->linfo.prev_spaces = 0; + break; + case HTML_N_PRE_INT: + check_rowcol(tbl); + pushdata(tbl, tbl->row, tbl->col, line); + mode->pre_mode &= ~TBLM_PRE_INT; + break; + case HTML_IMG: + tok = process_img(parse_tag(line + 3)); + feed_table1(tbl, tok, mode, width); + break; +#ifdef NEW_FORM + case HTML_FORM: + process_form(parse_tag(line)); + break; + case HTML_N_FORM: + process_n_form(); + break; +#else /* not NEW_FORM */ + case HTML_FORM: + case HTML_N_FORM: + check_rowcol(tbl); + pushdata(tbl, tbl->row, tbl->col, "<br>"); + setwidth(tbl, mode); + clearcontentssize(tbl, mode); + if (line[1] == '/') { + tok = Strnew_charp("</form_int "); + Strcat_charp(tok, line + 6); + } + else { + tok = Strnew_charp("<form_int "); + Strcat_charp(tok, line + 5); + } + pushdata(tbl, tbl->row, tbl->col, tok->ptr); + break; +#endif /* not NEW_FORM */ + case HTML_INPUT: + tmp = process_input(parse_tag(line + 6)); + feed_table1(tbl, tmp, mode, width); + break; + case HTML_SELECT: + t_arg = parse_tag(line + 7); + tmp = process_select(t_arg); + feed_table1(tbl, tmp, mode, width); +#ifdef MENU_SELECT + if (!tag_exists(t_arg, "multiple")) /* non-multiple select */ + mode->pre_mode |= TBLM_INSELECT; +#endif /* MENU_SELECT */ + break; + case HTML_N_SELECT: +#ifdef MENU_SELECT + mode->pre_mode &= ~TBLM_INSELECT; +#endif /* MENU_SELECT */ + tmp = process_n_select(); + feed_table1(tbl, tmp, mode, width); + break; + case HTML_OPTION: + tmp = process_option(parse_tag(line + 7)); + if (tmp) + feed_table1(tbl, tmp, mode, width); +#ifdef MENU_SELECT + else + feed_select(line); +#endif /* MENU_SELECT */ + break; + case HTML_TEXTAREA: + w = 0; + check_rowcol(tbl); + 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(parse_tag(line + 9), w); + feed_table1(tbl, tmp, mode, width); + mode->pre_mode |= TBLM_INTXTA; + break; + case HTML_N_TEXTAREA: + mode->pre_mode &= ~TBLM_INTXTA; + tmp = process_n_textarea(); + feed_table1(tbl, tmp, mode, width); + break; + case HTML_A: + anchor = NULL; + check_rowcol(tbl); + t_arg = parse_tag(line + 2); + i = 0; + for (t = t_arg; t; t = t->next) { + if (strcasecmp(t->arg, "href") == 0 && t->value) { + anchor = Strnew_charp(t->value); + } + else if (strcasecmp(t->arg, "hseq") == 0 && t->value) { + i = atoi(t->value); + } + } + if (i == 0 && anchor) { + Str tmp = process_anchor(line); + pushdata(tbl, tbl->row, tbl->col, tmp->ptr); + if (mode->pre_mode & (TBLM_PRE | TBLM_NOBR)) + addcontentssize(tbl, 1); + else + check_minimum0(tbl, 1); + setwidth(tbl, mode); + } + else { + pushdata(tbl, tbl->row, tbl->col, line); + } + break; + case HTML_DEL: + case HTML_N_DEL: + case HTML_INS: + case HTML_N_INS: + pushdata(tbl, tbl->row, tbl->col, line); + i = 5; + check_minimum0(tbl, i); + addcontentssize(tbl, i); + setwidth(tbl, mode); + break; + case HTML_DUMMY_TABLE: + id = -1; + w = 0; + t_arg = parse_tag(line + 12); + for (t = t_arg; t; t = t->next) { + if (!strcasecmp(t->arg, "id") && t->value) { + id = atoi(t->value); + } + else if (!strcasecmp(t->arg, "width") && t->value) { + w = atoi(t->value); + } + } + if (id >= 0) { + setwidth(tbl, mode); + check_rowcol(tbl); + pushdata(tbl, tbl->row, tbl->col, line); + clearcontentssize(tbl, mode); + addcontentssize(tbl, maximum_table_width(tbl->tables[id].ptr)); + check_minimum0(tbl, minimum_table_width(tbl->tables[id].ptr)); + if (w > 0) + check_minimum0(tbl, w); + else + check_minimum0(tbl, fixed_table_width(tbl->tables[id].ptr)); + setwidth0(tbl, mode); + clearcontentssize(tbl, mode); + } + break; + case HTML_CAPTION: + mode->caption = 1; + break; + case HTML_N_CAPTION: + mode->caption = 0; + 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; + default: + /* unknown tag: put into table */ + return TAG_ACTION_FEED; + } + return TAG_ACTION_NONE; +} + + +int +feed_table(struct table *tbl, char **tline, struct table_mode *mode, int width) +{ + int i; + char *line; + char *p; + Str tok, tmp; + struct table_linfo *linfo = &tbl->linfo; + + if (*tline == NULL || **tline == '\0') + return -1; + + tok = Strnew(); + if (tbl->suspended_input->length > 0) { + Strcat_charp(tbl->suspended_input, *tline); + *tline = tbl->suspended_input->ptr; + tbl->suspended_input = Strnew(); + tbl->status = R_ST_NORMAL; + } + while (read_token(tok, tline, &tbl->status, mode->pre_mode & TBLM_PREMODE, 0)) { + if (ST_IS_COMMENT(tbl->status)) { + continue; + } + else if (tbl->status != R_ST_NORMAL) { + /* line ended within tag */ + int l = tok->length; + if (tok->ptr[l - 1] == '\n') { + Strchop(tok); + if (ST_IS_REAL_TAG(tbl->status)) + Strcat_char(tok, ' '); + } + tbl->suspended_input = Strdup(tok); + return -1; + } + line = tok->ptr; + if (mode->caption && !TAG_IS(line, "</caption", 9)) { + Strcat(tbl->caption, tok); + continue; + } + if (*line == '<') { + int action = feed_table_tag(tbl, line, mode, width); + if (action == TAG_ACTION_NONE) + continue; + else if (action == TAG_ACTION_N_TABLE) + return 0; + else if (action == TAG_ACTION_TABLE) { + *tline -= tok->length; + return 1; + } + } + if (mode->pre_mode & TBLM_IGNORE) + continue; + if (mode->pre_mode & TBLM_INTXTA) { + feed_textarea(line); + continue; + } +#ifdef MENU_SELECT + if (mode->pre_mode & TBLM_INSELECT) { + feed_select(line); + continue; + } +#endif /* MENU_SELECT */ + /* convert &...; if it is latin-1 character */ + if (!(*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 { + q = p; + r = getescapecmd(&p); + if (r != NULL && ((*r & 0x80) || IS_CNTRL(*r))) { + /* latin-1 character */ + Strcat_charp(tmp, r); + } + else { + Strcat_char(tmp, *q); + p = q + 1; + } + } + } + else { + Strcat_char(tmp, *p); + p++; + } + } + line = tmp->ptr; + } + if (!(mode->pre_mode & (TBLM_PRE | TBLM_PRE_INT))) { + if (!(tbl->flag & TBL_IN_COL)) { + linfo->prev_spaces = -1; + linfo->prev_ctype = PC_ASCII; + } + if (linfo->prev_spaces != 0) + while (IS_SPACE(*line)) + line++; + if (*line == '\0') + continue; + check_rowcol(tbl); + 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); + if (mode->pre_mode & TBLM_PRE_INT && mode->nobr_offset < 0) + mode->nobr_offset = tbl->tabcontentssize; + 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); + } + if (ST_IS_COMMENT(tbl->status)) + return 2; + else + return -1; +} + +void +feed_table1(struct table *tbl, Str tok, struct table_mode *mode, int width) +{ + char *p; + if (!tok) + return; + p = tok->ptr; + feed_table(tbl, &p, mode, width); +} + +void +pushTable(struct table *tbl, struct table *tbl1) +{ + int col; + int row; + + check_rowcol(tbl); + 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 = newTextList(); + 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 */ |