/* -*- 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 #include #include #include #ifdef __EMX__ #include #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 TYPE=1>+", "<_RULE TYPE=2>+", "<_RULE TYPE=3>+", "<_RULE TYPE=4>+", "<_RULE TYPE=5>|", "<_RULE TYPE=6>+", "<_RULE TYPE=7>07", "<_RULE TYPE=8>+", "<_RULE TYPE=9>+", "<_RULE TYPE=10>-", "<_RULE TYPE=11>0B", "<_RULE TYPE=12>+", "<_RULE TYPE=13>0D", "<_RULE TYPE=14>0E", "<_RULE TYPE=15> "}; static char **ruleB = rule; #define TN_VERTICALBAR "<_RULE TYPE=5>|" #define HORIZONTALBAR "<_RULE TYPE=10>-" #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)) /* */ #define sigma_td_nw(a) (32*weight2(a)) /* */ #define sigma_table(a) (0.25*weight2(a)) /* */ #define sigma_table_nw(a) (2*weight2(a)) /* */ #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, "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, "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, "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, "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, ""); } 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, "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("
", &henv); HTMLlineproc1(t->caption->ptr, &henv); HTMLlineproc1("
", &henv); tl = henv.buf; if (tl->nitem > 0) { TextListItem *ti; tmp = Strnew_charp("
");
	for (ti = tl->first; ti != NULL; ti = ti->next) {
	    Strcat_charp(tmp, ti->ptr);
	    Strcat_char(tmp, '\n');
	}
	Strcat_charp(tmp, "
"); 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("
", 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, "");
		maxheight += 1;
		break;
	    case BORDER_NONE:
	    case BORDER_NOWIN:
		Strcat_charp(renderbuf, "");
		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(" ", h_env);
    HTMLlineproc1("
", 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 and in * * . */ 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, "
"); setwidth(tbl, mode); clearcontentssize(tbl, mode); if (line[1] == '/') { tok = Strnew_charp("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, 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 { /*
 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 */