1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3 Written by Ben Pfaff <blp@gnu.org>.
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 #include <libpspp/message.h>
27 #include <libpspp/alloc.h>
28 #include <libpspp/compiler.h>
29 #include <data/format.h>
30 #include <libpspp/magic.h>
31 #include <libpspp/misc.h>
34 #include <libpspp/pool.h>
36 #include <data/variable.h>
39 #define _(msgid) gettext (msgid)
41 #include <libpspp/debug-print.h>
43 struct som_table_class tab_table_class;
44 static char *command_name;
46 /* Returns the font to use for a cell with the given OPTIONS. */
48 options_to_font (unsigned options)
50 return (options & TAB_FIX ? OUTP_FIXED
51 : options & TAB_EMPH ? OUTP_EMPHASIS
55 /* Creates a table with NC columns and NR rows. */
57 tab_create (int nc, int nr, int reallocable UNUSED)
61 t = pool_create_container (struct tab_table, container);
62 t->col_style = TAB_COL_NONE;
68 t->l = t->r = t->t = t->b = 0;
70 t->cc = pool_nmalloc (t->container, nr * nc, sizeof *t->cc);
71 t->ct = pool_malloc (t->container, nr * nc);
72 memset (t->ct, TAB_EMPTY, nc * nr);
74 t->rh = pool_nmalloc (t->container, nc, nr + 1);
75 memset (t->rh, 0, nc * (nr + 1));
77 t->hrh = pool_nmalloc (t->container, nr + 1, sizeof *t->hrh);
78 memset (t->hrh, 0, sizeof *t->hrh * (nr + 1));
80 t->rv = pool_nmalloc (t->container, nr, nc + 1);
81 memset (t->rv, UCHAR_MAX, nr * (nc + 1));
83 t->wrv = pool_nmalloc (t->container, nc + 1, sizeof *t->wrv);
84 memset (t->wrv, 0, sizeof *t->wrv * (nc + 1));
88 t->col_ofs = t->row_ofs = 0;
93 /* Destroys table T. */
95 tab_destroy (struct tab_table *t)
98 pool_destroy (t->container);
101 /* Sets the width and height of a table, in columns and rows,
102 respectively. Use only to reduce the size of a table, since it
103 does not change the amount of allocated memory. */
105 tab_resize (struct tab_table *t, int nc, int nr)
110 assert (nc + t->col_ofs <= t->cf);
111 t->nc = nc + t->col_ofs;
115 assert (nr + t->row_ofs <= t->nr);
116 t->nr = nr + t->row_ofs;
120 /* Changes either or both dimensions of a table. Consider using the
121 above routine instead if it won't waste a lot of space.
123 Changing the number of columns in a table is particularly expensive
124 in space and time. Avoid doing such. FIXME: In fact, transferring
125 of rules isn't even implemented yet. */
127 tab_realloc (struct tab_table *t, int nc, int nr)
135 tab_offset (t, 0, 0);
142 assert (nc == t->nc);
146 int mr1 = min (nr, t->nr);
147 int mc1 = min (nc, t->nc);
149 struct fixed_string *new_cc;
150 unsigned char *new_ct;
153 new_cc = pool_nmalloc (t->container, nr * nc, sizeof *new_cc);
154 new_ct = pool_malloc (t->container, nr * nc);
155 for (r = 0; r < mr1; r++)
157 memcpy (&new_cc[r * nc], &t->cc[r * t->nc], mc1 * sizeof *t->cc);
158 memcpy (&new_ct[r * nc], &t->ct[r * t->nc], mc1);
159 memset (&new_ct[r * nc + t->nc], TAB_EMPTY, nc - t->nc);
161 pool_free (t->container, t->cc);
162 pool_free (t->container, t->ct);
167 else if (nr != t->nr)
169 t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc);
170 t->ct = pool_realloc (t->container, t->ct, nr * nc);
172 t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
173 t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
177 memset (&t->rh[nc * (t->nr + 1)], TAL_0, (nr - t->nr) * nc);
178 memset (&t->rv[(nc + 1) * t->nr], UCHAR_MAX,
179 (nr - t->nr) * (nc + 1));
183 memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr));
189 tab_offset (t, co, ro);
192 /* Sets the number of header rows on each side of TABLE to L on the
193 left, R on the right, T on the top, B on the bottom. Header rows
194 are repeated when a table is broken across multiple columns or
197 tab_headers (struct tab_table *table, int l, int r, int t, int b)
199 assert (table != NULL);
200 assert (l < table->nc);
201 assert (r < table->nc);
202 assert (t < table->nr);
203 assert (b < table->nr);
212 /* Set up table T so that, when it is an appropriate size, it will be
213 displayed across the page in columns.
215 STYLE is a TAB_COL_* constant. GROUP is the number of rows to take
218 tab_columns (struct tab_table *t, int style, int group)
221 t->col_style = style;
222 t->col_group = group;
227 /* Draws a vertical line to the left of cells at horizontal position X
228 from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
230 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
235 if (x + t->col_ofs < 0 || x + t->col_ofs > t->nc
236 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
237 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
239 printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
240 "table size (%d,%d)\n"),
241 x, t->col_ofs, x + t->col_ofs,
242 y1, t->row_ofs, y1 + t->row_ofs,
243 y2, t->row_ofs, y2 + t->row_ofs,
257 assert (y2 <= t->nr);
262 for (y = y1; y <= y2; y++)
263 t->rv[x + (t->cf + 1) * y] = style;
267 /* Draws a horizontal line above cells at vertical position Y from X1
268 to X2 inclusive in style STYLE, if style is not -1. */
270 tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
287 for (x = x1; x <= x2; x++)
288 t->rh[x + t->cf * y] = style;
292 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
293 lines of style F_H and vertical lines of style F_V. Fills the
294 interior of the box with horizontal lines of style I_H and vertical
295 lines of style I_V. Any of the line styles may be -1 to avoid
296 drawing those lines. This is distinct from 0, which draws a null
299 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
300 int x1, int y1, int x2, int y2)
305 if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc
306 || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= t->nc
307 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
308 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
310 printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
311 "in table size (%d,%d)\n"),
312 x1, t->col_ofs, x1 + t->col_ofs,
313 y1, t->row_ofs, y1 + t->row_ofs,
314 x2, t->col_ofs, x2 + t->col_ofs,
315 y2, t->row_ofs, y2 + t->row_ofs,
336 for (x = x1; x <= x2; x++)
338 t->rh[x + t->cf * y1] = f_h;
339 t->rh[x + t->cf * (y2 + 1)] = f_h;
345 for (y = y1; y <= y2; y++)
347 t->rv[x1 + (t->cf + 1) * y] = f_v;
348 t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
356 for (y = y1 + 1; y <= y2; y++)
360 for (x = x1; x <= x2; x++)
361 t->rh[x + t->cf * y] = i_h;
368 for (x = x1 + 1; x <= x2; x++)
372 for (y = y1; y <= y2; y++)
373 t->rv[x + (t->cf + 1) * y] = i_v;
378 /* Formats text TEXT and arguments ARGS as indicated in OPT and sets
379 the resultant string into S in TABLE's pool. */
381 text_format (struct tab_table *table, int opt, const char *text, va_list args,
382 struct fixed_string *s)
386 assert (table != NULL && text != NULL && s != NULL);
388 if (opt & TAT_PRINTF)
389 text = tmp = xvasprintf (text, args);
391 ls_create_buffer (s, text, strlen (text));
392 pool_register (table->container, free, s->string);
397 /* Set the title of table T to TITLE, which is formatted as if
398 passed to printf(). */
400 tab_title (struct tab_table *t, const char *title, ...)
404 assert (t != NULL && title != NULL);
405 va_start (args, title);
406 text_format (t, TAT_PRINTF, title, args, &t->title);
410 /* Set DIM_FUNC as the dimension function for table T. */
412 tab_dim (struct tab_table *t, tab_dim_func *dim_func)
414 assert (t != NULL && t->dim == NULL);
418 /* Returns the natural width of column C in table T for driver D, that
419 is, the smallest width necessary to display all its cells without
420 wrapping. The width will be no larger than the page width minus
421 left and right rule widths. */
423 tab_natural_width (struct tab_table *t, struct outp_driver *d, int c)
427 assert (t != NULL && c >= 0 && c < t->nc);
431 for (width = r = 0; r < t->nr; r++)
433 struct outp_text text;
434 unsigned char opt = t->ct[c + r * t->cf];
437 if (opt & (TAB_JOIN | TAB_EMPTY))
440 text.string = t->cc[c + r * t->cf];
441 assert (!ls_null_p (&text.string));
442 text.justification = OUTP_LEFT;
443 text.font = options_to_font (opt);
444 text.h = text.v = INT_MAX;
446 d->class->text_metrics (d, &text, &w, NULL);
454 width = d->prop_em_width * 8;
456 printf ("warning: table column %d contains no data.\n", c);
461 const int clamp = d->width - t->wrv[0] - t->wrv[t->nc];
470 /* Returns the natural height of row R in table T for driver D, that
471 is, the minimum height necessary to display the information in the
472 cell at the widths set for each column. */
474 tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
478 assert (t != NULL && r >= 0 && r < t->nr);
483 for (height = d->font_height, c = 0; c < t->nc; c++)
485 struct outp_text text;
486 unsigned char opt = t->ct[c + r * t->cf];
489 assert (t->w[c] != NOT_INT);
490 if (opt & (TAB_JOIN | TAB_EMPTY))
493 text.string = t->cc[c + r * t->cf];
494 assert (!ls_null_p (&text.string));
495 text.justification = OUTP_LEFT;
496 text.font = options_to_font (opt);
499 d->class->text_metrics (d, &text, NULL, &h);
509 /* Callback function to set all columns and rows to their natural
510 dimensions. Not really meant to be called directly. */
512 tab_natural_dimensions (struct tab_table *t, struct outp_driver *d)
518 for (i = 0; i < t->nc; i++)
519 t->w[i] = tab_natural_width (t, d, i);
521 for (i = 0; i < t->nr; i++)
522 t->h[i] = tab_natural_height (t, d, i);
528 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
529 from V, displayed with format spec F. */
531 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
532 const union value *v, const struct fmt_spec *f)
536 assert (table != NULL && v != NULL && f != NULL);
538 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
539 || c + table->col_ofs >= table->nc
540 || r + table->row_ofs >= table->nr)
542 printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
544 c, table->col_ofs, c + table->col_ofs,
545 r, table->row_ofs, r + table->row_ofs,
546 table->nc, table->nr);
551 contents = pool_alloc (table->container, f->w);
552 ls_init (&table->cc[c + r * table->cf], contents, f->w);
553 table->ct[c + r * table->cf] = opt;
555 data_out (contents, f, v);
558 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
559 with NDEC decimal places. */
561 tab_float (struct tab_table *table, int c, int r, unsigned char opt,
562 double val, int w, int d)
568 union value double_value;
570 assert (table != NULL && w <= 40);
573 assert (c < table->nc);
575 assert (r < table->nr);
577 f = make_output_format (FMT_F, w, d);
580 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
581 || c + table->col_ofs >= table->nc
582 || r + table->row_ofs >= table->nr)
584 printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
586 c, table->col_ofs, c + table->col_ofs,
587 r, table->row_ofs, r + table->row_ofs,
588 table->nc, table->nr);
593 double_value.f = val;
594 data_out (buf, &f, &double_value);
597 while (isspace ((unsigned char) *cp) && cp < &buf[w])
599 f.w = w - (cp - buf);
601 contents = pool_alloc (table->container, f.w);
602 ls_init (&table->cc[c + r * table->cf], contents, f.w);
603 table->ct[c + r * table->cf] = opt;
604 memcpy (contents, cp, f.w);
607 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
610 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
614 assert (table != NULL && text != NULL);
618 assert (c < table->nc);
619 assert (r < table->nr);
623 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
624 || c + table->col_ofs >= table->nc
625 || r + table->row_ofs >= table->nr)
627 printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
629 c, table->col_ofs, c + table->col_ofs,
630 r, table->row_ofs, r + table->row_ofs,
631 table->nc, table->nr);
636 va_start (args, text);
637 text_format (table, opt, text, args, &table->cc[c + r * table->cf]);
638 table->ct[c + r * table->cf] = opt;
642 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
643 options OPT to have text value TEXT. */
645 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
646 unsigned opt, const char *text, ...)
648 struct tab_joined_cell *j;
650 assert (table != NULL && text != NULL);
652 assert (x1 + table->col_ofs >= 0);
653 assert (y1 + table->row_ofs >= 0);
656 assert (y2 + table->row_ofs < table->nr);
657 assert (x2 + table->col_ofs < table->nc);
660 if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
661 || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
662 || x2 < x1 || x2 + table->col_ofs >= table->nc
663 || y2 < y2 || y2 + table->row_ofs >= table->nr)
665 printf ("tab_joint_text(): bad cell "
666 "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
667 x1, table->col_ofs, x1 + table->col_ofs,
668 y1, table->row_ofs, y1 + table->row_ofs,
669 x2, table->col_ofs, x2 + table->col_ofs,
670 y2, table->row_ofs, y2 + table->row_ofs,
671 table->nc, table->nr);
676 tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
678 j = pool_alloc (table->container, sizeof *j);
680 j->x1 = x1 + table->col_ofs;
681 j->y1 = y1 + table->row_ofs;
682 j->x2 = ++x2 + table->col_ofs;
683 j->y2 = ++y2 + table->row_ofs;
688 va_start (args, text);
689 text_format (table, opt, text, args, &j->contents);
696 struct fixed_string *cc = &table->cc[x1 + y1 * table->cf];
697 unsigned char *ct = &table->ct[x1 + y1 * table->cf];
698 const int ofs = table->cf - (x2 - x1);
702 for (y = y1; y < y2; y++)
706 for (x = x1; x < x2; x++)
708 ls_init (cc++, (char *) j, 0);
718 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
720 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
721 struct fixed_string *string)
723 assert (table != NULL && string != NULL);
726 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
727 || c + table->col_ofs >= table->nc
728 || r + table->row_ofs >= table->nr)
730 printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
732 c, table->col_ofs, c + table->col_ofs,
733 r, table->row_ofs, r + table->row_ofs,
734 table->nc, table->nr);
739 table->cc[c + r * table->cf] = *string;
740 table->ct[c + r * table->cf] = opt;
745 /* Sets the widths of all the columns and heights of all the rows in
746 table T for driver D. */
748 nowrap_dim (struct tab_table *t, struct outp_driver *d)
750 t->w[0] = tab_natural_width (t, d, 0);
751 t->h[0] = d->font_height;
754 /* Sets the widths of all the columns and heights of all the rows in
755 table T for driver D. */
757 wrap_dim (struct tab_table *t, struct outp_driver *d)
759 t->w[0] = tab_natural_width (t, d, 0);
760 t->h[0] = tab_natural_height (t, d, 0);
763 /* Outputs text BUF as a table with a single cell having cell options
764 OPTIONS, which is a combination of the TAB_* and TAT_*
767 tab_output_text (int options, const char *buf, ...)
769 struct tab_table *t = tab_create (1, 1, 0);
770 char *tmp_buf = NULL;
772 if (options & TAT_PRINTF)
776 va_start (args, buf);
777 buf = tmp_buf = xvasprintf (buf, args);
781 tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
782 tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
783 tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim);
789 /* Set table flags to FLAGS. */
791 tab_flags (struct tab_table *t, unsigned flags)
797 /* Easy, type-safe way to submit a tab table to som. */
799 tab_submit (struct tab_table *t)
804 s.class = &tab_table_class;
813 /* Set table row and column offsets for all functions that affect
816 tab_offset (struct tab_table *t, int col, int row)
822 if (row < -1 || row >= t->nr)
824 printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
827 if (col < -1 || col >= t->nc)
829 printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
835 diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
837 diff += (col - t->col_ofs), t->col_ofs = col;
843 /* Increment the row offset by one. If the table is too small,
844 increase its size. */
846 tab_next_row (struct tab_table *t)
851 if (++t->row_ofs >= t->nr)
852 tab_realloc (t, -1, t->nr * 4 / 3);
855 static struct tab_table *t;
856 static struct outp_driver *d;
859 /* Set the current table to TABLE. */
861 tabi_table (struct som_entity *table)
863 assert (table != NULL);
864 assert (table->type == SOM_TABLE);
867 tab_offset (t, 0, 0);
869 assert (t->w == NULL && t->h == NULL);
870 t->w = pool_nalloc (t->container, t->nc, sizeof *t->w);
871 t->h = pool_nalloc (t->container, t->nr, sizeof *t->h);
874 /* Returns the line style to use for spacing purposes for a rule
875 of the given TYPE. */
876 static enum outp_line_style
877 rule_to_spacing_type (unsigned char type)
885 return OUTP_L_SINGLE;
887 return OUTP_L_DOUBLE;
893 /* Set the current output device to DRIVER. */
895 tabi_driver (struct outp_driver *driver)
900 assert (driver != NULL);
903 /* Figure out sizes of rules. */
904 for (r = 0; r <= t->nr; r++)
907 for (c = 0; c < t->nc; c++)
909 unsigned char rh = t->rh[c + r * t->cf];
910 int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
917 for (c = 0; c <= t->nc; c++)
920 for (r = 0; r < t->nr; r++)
922 unsigned char *rv = &t->rv[c + r * (t->cf + 1)];
924 if (*rv == UCHAR_MAX)
925 *rv = c != 0 && c != t->nc ? TAL_GAP : TAL_0;
926 w = driver->vert_line_width[rule_to_spacing_type (*rv)];
934 for (i = 0; i < t->nr; i++)
936 for (i = 0; i < t->nc; i++)
940 assert (t->dim != NULL);
947 for (i = 0; i < t->nr; i++)
951 printf ("Table row %d height not initialized.\n", i);
954 assert (t->h[i] > 0);
957 for (i = 0; i < t->nc; i++)
961 printf ("Table column %d width not initialized.\n", i);
964 assert (t->w[i] > 0);
969 /* Add up header sizes. */
970 for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
971 t->wl += t->w[i] + t->wrv[i + 1];
972 for (i = 0, t->ht = t->hrh[0]; i < t->t; i++)
973 t->ht += t->h[i] + t->hrh[i + 1];
974 for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++)
975 t->wr += t->w[i] + t->wrv[i + 1];
976 for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
977 t->hb += t->h[i] + t->hrh[i + 1];
980 if (!(t->flags & SOMF_NO_TITLE))
981 t->ht += d->font_height;
984 /* Return the number of columns and rows in the table into N_COLUMNS
985 and N_ROWS, respectively. */
987 tabi_count (int *n_columns, int *n_rows)
989 assert (n_columns != NULL && n_rows != NULL);
994 static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual);
996 /* Return the horizontal and vertical size of the entire table,
997 including headers, for the current output device, into HORIZ and
1000 tabi_area (int *horiz, int *vert)
1002 assert (horiz != NULL && vert != NULL);
1007 for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
1008 c < t->nc - t->r; c++)
1009 w += t->w[c] + t->wrv[c];
1015 for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
1016 r < t->nr - t->b; r++)
1017 h += t->h[r] + t->hrh[r];
1022 /* Return the column style for this table into STYLE. */
1024 tabi_columns (int *style)
1026 assert (style != NULL);
1027 *style = t->col_style;
1030 /* Return the number of header rows/columns on the left, right, top,
1031 and bottom sides into HL, HR, HT, and HB, respectively. */
1033 tabi_headers (int *hl, int *hr, int *ht, int *hb)
1035 assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL);
1042 /* Determines the number of rows or columns (including appropriate
1043 headers), depending on CUMTYPE, that will fit into the space
1044 specified. Takes rows/columns starting at index START and attempts
1045 to fill up available space MAX. Returns in END the index of the
1046 last row/column plus one; returns in ACTUAL the actual amount of
1047 space the selected rows/columns (including appropriate headers)
1050 tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
1057 assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1058 if (cumtype == SOM_ROWS)
1060 assert (start >= 0 && start < t->nr);
1063 r = &t->hrh[start + 1];
1064 total = t->ht + t->hb;
1068 assert (start >= 0 && start < t->nc);
1071 r = &t->wrv[start + 1];
1072 total = t->wl + t->wr;
1088 for (x = start + 1; x < n; x++)
1090 int amt = *d++ + *r++;
1108 /* Return flags set for the current table into FLAGS. */
1110 tabi_flags (unsigned *flags)
1112 assert (flags != NULL);
1116 /* Returns true if the table will fit in the given page WIDTH,
1119 tabi_fits_width (int width)
1123 for (i = t->l; i < t->nc - t->r; i++)
1124 if (t->wl + t->wr + t->w[i] > width)
1130 /* Returns true if the table will fit in the given page LENGTH,
1133 tabi_fits_length (int length)
1137 for (i = t->t; i < t->nr - t->b; i++)
1138 if (t->ht + t->hb + t->h[i] > length)
1144 /* Sets the number of header rows/columns on the left, right, top,
1145 and bottom sides to HL, HR, HT, and HB, respectively. */
1147 tabi_set_headers (int hl, int hr, int ht, int hb)
1155 /* Render title for current table, with major index X and minor index
1156 Y. Y may be zero, or X and Y may be zero, but X should be nonzero
1159 tabi_title (int x, int y)
1164 if (t->flags & SOMF_NO_TITLE)
1167 cp = spprintf (buf, "%d.%d", table_num, subtable_num);
1169 cp = spprintf (cp, "(%d:%d)", x, y);
1171 cp = spprintf (cp, "(%d)", x);
1172 if (command_name != NULL)
1173 cp = spprintf (cp, " %s", command_name);
1174 cp = stpcpy (cp, ". ");
1175 if (!ls_empty_p (&t->title))
1177 memcpy (cp, ls_c_str (&t->title), ls_length (&t->title));
1178 cp += ls_length (&t->title);
1183 struct outp_text text;
1185 text.font = OUTP_PROPORTIONAL;
1186 text.justification = OUTP_LEFT;
1187 ls_init (&text.string, buf, cp - buf);
1189 text.v = d->font_height;
1192 d->class->text_draw (d, &text);
1196 static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
1198 /* Renders columns C0...C1, plus headers, of rows R0...R1,
1199 at the given vertical position Y.
1200 C0 and C1 count vertical rules as columns,
1201 but R0 and R1 do not count horizontal rules as rows.
1202 Returns the vertical position after rendering. */
1204 render_rows (int y, int c0, int c1, int r0, int r1)
1207 for (r = r0; r < r1; r++)
1210 x = render_strip (x, y, r, 0, t->l * 2 + 1, r0, r1);
1211 x = render_strip (x, y, r, c0 * 2 + 1, c1 * 2, r0, r1);
1212 x = render_strip (x, y, r, (t->nc - t->r) * 2, t->nc * 2 + 1, r0, r1);
1213 y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2];
1218 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1219 current position on the current output device. */
1221 tabi_render (int c0, int r0, int c1, int r1)
1228 if (!(t->flags & SOMF_NO_TITLE))
1229 y += d->font_height;
1231 y = render_rows (y, c0, c1, 0, t->t * 2 + 1);
1232 y = render_rows (y, c0, c1, r0 * 2 + 1, r1 * 2);
1233 y = render_rows (y, c0, c1, (t->nr - t->b) * 2, t->nr * 2 + 1);
1236 struct som_table_class tab_table_class =
1262 static enum outp_justification
1263 translate_justification (unsigned int opt)
1265 switch (opt & TAB_ALIGN_MASK)
1278 /* Returns the line style to use for drawing a rule of the given
1280 static enum outp_line_style
1281 rule_to_draw_type (unsigned char type)
1289 return OUTP_L_SINGLE;
1291 return OUTP_L_DOUBLE;
1297 /* Returns the horizontal rule at the given column and row. */
1299 get_hrule (int c, int r)
1301 return t->rh[c + r * t->cf];
1304 /* Returns the vertical rule at the given column and row. */
1306 get_vrule (int c, int r)
1308 return t->rv[c + r * (t->cf + 1)];
1311 /* Renders the horizontal rule at the given column and row
1312 at (X,Y) on the page. */
1314 render_horz_rule (int x, int y, int c, int r)
1316 enum outp_line_style style = rule_to_draw_type (get_hrule (c, r));
1317 if (style != OUTP_L_NONE)
1318 d->class->line (d, x, y, x + t->w[c], y + t->hrh[r],
1319 OUTP_L_NONE, style, OUTP_L_NONE, style);
1322 /* Renders the vertical rule at the given column and row
1323 at (X,Y) on the page. */
1325 render_vert_rule (int x, int y, int c, int r)
1327 enum outp_line_style style = rule_to_draw_type (get_vrule (c, r));
1328 if (style != OUTP_L_NONE)
1329 d->class->line (d, x, y, x + t->wrv[c], y + t->h[r],
1330 style, OUTP_L_NONE, style, OUTP_L_NONE);
1333 /* Renders the rule intersection at the given column and row
1334 at (X,Y) on the page. */
1336 render_rule_intersection (int x, int y, int c, int r)
1338 /* Bounds of intersection. */
1341 int x1 = x + t->wrv[c];
1342 int y1 = y + t->hrh[r];
1344 /* Lines on each side of intersection. */
1345 int top = r > 0 ? get_vrule (c, r - 1) : TAL_0;
1346 int left = c > 0 ? get_hrule (c - 1, r) : TAL_0;
1347 int bottom = r < t->nr ? get_vrule (c, r) : TAL_0;
1348 int right = c < t->nc ? get_hrule (c, r) : TAL_0;
1350 /* Output style for each line. */
1351 enum outp_line_style o_top = rule_to_draw_type (top);
1352 enum outp_line_style o_left = rule_to_draw_type (left);
1353 enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1354 enum outp_line_style o_right = rule_to_draw_type (right);
1356 if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1357 || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1358 d->class->line (d, x0, y0, x1, y1, o_top, o_left, o_bottom, o_right);
1361 /* Returns the width of columns C1...C2 exclusive,
1362 including interior but not exterior rules. */
1364 strip_width (int c1, int c2)
1369 for (c = c1; c < c2; c++)
1370 width += t->w[c] + t->wrv[c + 1];
1372 width -= t->wrv[c2];
1376 /* Returns the height of rows R1...R2 exclusive,
1377 including interior but not exterior rules. */
1379 strip_height (int r1, int r2)
1384 for (r = r1; r < r2; r++)
1385 height += t->h[r] + t->hrh[r + 1];
1387 height -= t->hrh[r2];
1391 /* Renders the cell at the given column and row at (X,Y) on the
1392 page. Also renders joined cells that extend as far to the
1393 right as C1 and as far down as R1. */
1395 render_cell (int x, int y, int c, int r, int c1, int r1)
1397 const int index = c + (r * t->cf);
1398 unsigned char type = t->ct[index];
1399 struct fixed_string *content = &t->cc[index];
1401 if (!(type & TAB_JOIN))
1403 if (!(type & TAB_EMPTY))
1405 struct outp_text text;
1406 text.font = options_to_font (type);
1407 text.justification = translate_justification (type);
1408 text.string = *content;
1413 d->class->text_draw (d, &text);
1418 struct tab_joined_cell *j
1419 = (struct tab_joined_cell *) ls_c_str (content);
1421 if (j->hit != tab_hit)
1425 if (j->x1 == c && j->y1 == r)
1427 struct outp_text text;
1428 text.font = options_to_font (type);
1429 text.justification = translate_justification (type);
1430 text.string = j->contents;
1433 text.h = strip_width (j->x1, MIN (j->x2, c1));
1434 text.v = strip_height (j->y1, MIN (j->y2, r1));
1435 d->class->text_draw (d, &text);
1441 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1442 on row R, at (X,Y). Returns X position after rendering.
1443 Also renders joined cells that extend beyond that strip,
1444 cropping them to lie within rendering region (C0,R0)-(C1,R1).
1445 C0 and C1 count vertical rules as columns.
1446 R counts horizontal rules as rows, but R0 and R1 do not. */
1448 render_strip (int x, int y, int r, int c0, int c1, int r0 UNUSED, int r1)
1452 for (c = c0; c < c1; c++)
1456 render_cell (x, y, c / 2, r / 2, c1 / 2, r1);
1458 render_horz_rule (x, y, c / 2, r / 2);
1464 render_vert_rule (x, y, c / 2, r / 2);
1466 render_rule_intersection (x, y, c / 2, r / 2);
1473 /* Sets COMMAND_NAME as the name of the current command,
1474 for embedding in output. */
1476 tab_set_command_name (const char *command_name_)
1478 free (command_name);
1479 command_name = command_name_ ? xstrdup (command_name_) : NULL;