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/alloc.h>
27 #include <libpspp/assertion.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 const struct som_table_class tab_table_class;
42 static char *command_name;
44 /* Returns the font to use for a cell with the given OPTIONS. */
46 options_to_font (unsigned options)
48 return (options & TAB_FIX ? OUTP_FIXED
49 : options & TAB_EMPH ? OUTP_EMPHASIS
53 /* Creates a table with NC columns and NR rows. */
55 tab_create (int nc, int nr, int reallocable UNUSED)
59 t = pool_create_container (struct tab_table, container);
60 t->col_style = TAB_COL_NONE;
66 t->l = t->r = t->t = t->b = 0;
68 t->cc = pool_nmalloc (t->container, nr * nc, sizeof *t->cc);
69 t->ct = pool_malloc (t->container, nr * nc);
70 memset (t->ct, TAB_EMPTY, nc * nr);
72 t->rh = pool_nmalloc (t->container, nc, nr + 1);
73 memset (t->rh, 0, nc * (nr + 1));
75 t->rv = pool_nmalloc (t->container, nr, nc + 1);
76 memset (t->rv, UCHAR_MAX, nr * (nc + 1));
80 t->col_ofs = t->row_ofs = 0;
85 /* Destroys table T. */
87 tab_destroy (struct tab_table *t)
91 pool_destroy (t->container);
94 /* Sets the width and height of a table, in columns and rows,
95 respectively. Use only to reduce the size of a table, since it
96 does not change the amount of allocated memory. */
98 tab_resize (struct tab_table *t, int nc, int nr)
103 assert (nc + t->col_ofs <= t->cf);
104 t->nc = nc + t->col_ofs;
108 assert (nr + t->row_ofs <= t->nr);
109 t->nr = nr + t->row_ofs;
113 /* Changes either or both dimensions of a table. Consider using the
114 above routine instead if it won't waste a lot of space.
116 Changing the number of columns in a table is particularly expensive
117 in space and time. Avoid doing such. FIXME: In fact, transferring
118 of rules isn't even implemented yet. */
120 tab_realloc (struct tab_table *t, int nc, int nr)
128 tab_offset (t, 0, 0);
135 assert (nc == t->nc);
139 int mr1 = min (nr, t->nr);
140 int mc1 = min (nc, t->nc);
142 struct substring *new_cc;
143 unsigned char *new_ct;
146 new_cc = pool_nmalloc (t->container, nr * nc, sizeof *new_cc);
147 new_ct = pool_malloc (t->container, nr * nc);
148 for (r = 0; r < mr1; r++)
150 memcpy (&new_cc[r * nc], &t->cc[r * t->nc], mc1 * sizeof *t->cc);
151 memcpy (&new_ct[r * nc], &t->ct[r * t->nc], mc1);
152 memset (&new_ct[r * nc + t->nc], TAB_EMPTY, nc - t->nc);
154 pool_free (t->container, t->cc);
155 pool_free (t->container, t->ct);
160 else if (nr != t->nr)
162 t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc);
163 t->ct = pool_realloc (t->container, t->ct, nr * nc);
165 t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
166 t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
170 memset (&t->rh[nc * (t->nr + 1)], TAL_0, (nr - t->nr) * nc);
171 memset (&t->rv[(nc + 1) * t->nr], UCHAR_MAX,
172 (nr - t->nr) * (nc + 1));
176 memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr));
182 tab_offset (t, co, ro);
185 /* Sets the number of header rows on each side of TABLE to L on the
186 left, R on the right, T on the top, B on the bottom. Header rows
187 are repeated when a table is broken across multiple columns or
190 tab_headers (struct tab_table *table, int l, int r, int t, int b)
192 assert (table != NULL);
193 assert (l < table->nc);
194 assert (r < table->nc);
195 assert (t < table->nr);
196 assert (b < table->nr);
205 /* Set up table T so that, when it is an appropriate size, it will be
206 displayed across the page in columns.
208 STYLE is a TAB_COL_* constant. GROUP is the number of rows to take
211 tab_columns (struct tab_table *t, int style, int group)
214 t->col_style = style;
215 t->col_group = group;
220 /* Draws a vertical line to the left of cells at horizontal position X
221 from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
223 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
228 if (x + t->col_ofs < 0 || x + t->col_ofs > t->nc
229 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
230 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
232 printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
233 "table size (%d,%d)\n"),
234 x, t->col_ofs, x + t->col_ofs,
235 y1, t->row_ofs, y1 + t->row_ofs,
236 y2, t->row_ofs, y2 + t->row_ofs,
250 assert (y2 <= t->nr);
255 for (y = y1; y <= y2; y++)
256 t->rv[x + (t->cf + 1) * y] = style;
260 /* Draws a horizontal line above cells at vertical position Y from X1
261 to X2 inclusive in style STYLE, if style is not -1. */
263 tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
280 for (x = x1; x <= x2; x++)
281 t->rh[x + t->cf * y] = style;
285 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
286 lines of style F_H and vertical lines of style F_V. Fills the
287 interior of the box with horizontal lines of style I_H and vertical
288 lines of style I_V. Any of the line styles may be -1 to avoid
289 drawing those lines. This is distinct from 0, which draws a null
292 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
293 int x1, int y1, int x2, int y2)
298 if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc
299 || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= t->nc
300 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
301 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
303 printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
304 "in table size (%d,%d)\n"),
305 x1, t->col_ofs, x1 + t->col_ofs,
306 y1, t->row_ofs, y1 + t->row_ofs,
307 x2, t->col_ofs, x2 + t->col_ofs,
308 y2, t->row_ofs, y2 + t->row_ofs,
329 for (x = x1; x <= x2; x++)
331 t->rh[x + t->cf * y1] = f_h;
332 t->rh[x + t->cf * (y2 + 1)] = f_h;
338 for (y = y1; y <= y2; y++)
340 t->rv[x1 + (t->cf + 1) * y] = f_v;
341 t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
349 for (y = y1 + 1; y <= y2; y++)
353 for (x = x1; x <= x2; x++)
354 t->rh[x + t->cf * y] = i_h;
361 for (x = x1 + 1; x <= x2; x++)
365 for (y = y1; y <= y2; y++)
366 t->rv[x + (t->cf + 1) * y] = i_v;
371 /* Formats text TEXT and arguments ARGS as indicated in OPT in
372 TABLE's pool and returns the resultant string. */
373 static struct substring
374 text_format (struct tab_table *table, int opt, const char *text, va_list args)
376 assert (table != NULL && text != NULL);
378 return ss_cstr (opt & TAT_PRINTF
379 ? pool_vasprintf (table->container, text, args)
380 : pool_strdup (table->container, text));
383 /* Set the title of table T to TITLE, which is formatted as if
384 passed to printf(). */
386 tab_title (struct tab_table *t, const char *title, ...)
390 assert (t != NULL && title != NULL);
391 va_start (args, title);
392 t->title = xvasprintf (title, args);
396 /* Set DIM_FUNC as the dimension function for table T. */
398 tab_dim (struct tab_table *t, tab_dim_func *dim_func)
400 assert (t != NULL && t->dim == NULL);
404 /* Returns the natural width of column C in table T for driver D, that
405 is, the smallest width necessary to display all its cells without
406 wrapping. The width will be no larger than the page width minus
407 left and right rule widths. */
409 tab_natural_width (struct tab_table *t, struct outp_driver *d, int c)
413 assert (t != NULL && c >= 0 && c < t->nc);
417 for (width = r = 0; r < t->nr; r++)
419 struct outp_text text;
420 unsigned char opt = t->ct[c + r * t->cf];
423 if (opt & (TAB_JOIN | TAB_EMPTY))
426 text.string = t->cc[c + r * t->cf];
427 text.justification = OUTP_LEFT;
428 text.font = options_to_font (opt);
429 text.h = text.v = INT_MAX;
431 d->class->text_metrics (d, &text, &w, NULL);
439 /* FIXME: This is an ugly kluge to compensate for the fact
440 that we don't let joined cells contribute to column
442 width = d->prop_em_width * 8;
446 const int clamp = d->width - t->wrv[0] - t->wrv[t->nc];
455 /* Returns the natural height of row R in table T for driver D, that
456 is, the minimum height necessary to display the information in the
457 cell at the widths set for each column. */
459 tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
463 assert (t != NULL && r >= 0 && r < t->nr);
468 for (height = d->font_height, c = 0; c < t->nc; c++)
470 struct outp_text text;
471 unsigned char opt = t->ct[c + r * t->cf];
474 assert (t->w[c] != NOT_INT);
475 if (opt & (TAB_JOIN | TAB_EMPTY))
478 text.string = t->cc[c + r * t->cf];
479 text.justification = OUTP_LEFT;
480 text.font = options_to_font (opt);
483 d->class->text_metrics (d, &text, NULL, &h);
493 /* Callback function to set all columns and rows to their natural
494 dimensions. Not really meant to be called directly. */
496 tab_natural_dimensions (struct tab_table *t, struct outp_driver *d)
502 for (i = 0; i < t->nc; i++)
503 t->w[i] = tab_natural_width (t, d, i);
505 for (i = 0; i < t->nr; i++)
506 t->h[i] = tab_natural_height (t, d, i);
512 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
513 from V, displayed with format spec F. */
515 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
516 const union value *v, const struct fmt_spec *f)
520 assert (table != NULL && v != NULL && f != NULL);
522 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
523 || c + table->col_ofs >= table->nc
524 || r + table->row_ofs >= table->nr)
526 printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
528 c, table->col_ofs, c + table->col_ofs,
529 r, table->row_ofs, r + table->row_ofs,
530 table->nc, table->nr);
535 contents = pool_alloc (table->container, f->w);
536 table->cc[c + r * table->cf] = ss_buffer (contents, f->w);
537 table->ct[c + r * table->cf] = opt;
539 data_out (contents, f, v);
542 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
543 with NDEC decimal places. */
545 tab_float (struct tab_table *table, int c, int r, unsigned char opt,
546 double val, int w, int d)
552 union value double_value;
554 assert (table != NULL && w <= 40);
557 assert (c < table->nc);
559 assert (r < table->nr);
561 f = make_output_format (FMT_F, w, d);
564 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
565 || c + table->col_ofs >= table->nc
566 || r + table->row_ofs >= table->nr)
568 printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
570 c, table->col_ofs, c + table->col_ofs,
571 r, table->row_ofs, r + table->row_ofs,
572 table->nc, table->nr);
577 double_value.f = val;
578 data_out (buf, &f, &double_value);
581 while (isspace ((unsigned char) *cp) && cp < &buf[w])
583 f.w = w - (cp - buf);
585 contents = pool_alloc (table->container, f.w);
586 table->cc[c + r * table->cf] = ss_buffer (contents, f.w);
587 table->ct[c + r * table->cf] = opt;
588 memcpy (contents, cp, f.w);
591 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
594 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
598 assert (table != NULL && text != NULL);
602 assert (c < table->nc);
603 assert (r < table->nr);
607 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
608 || c + table->col_ofs >= table->nc
609 || r + table->row_ofs >= table->nr)
611 printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
613 c, table->col_ofs, c + table->col_ofs,
614 r, table->row_ofs, r + table->row_ofs,
615 table->nc, table->nr);
620 va_start (args, text);
621 table->cc[c + r * table->cf] = text_format (table, opt, text, args);
622 table->ct[c + r * table->cf] = opt;
626 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
627 options OPT to have text value TEXT. */
629 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
630 unsigned opt, const char *text, ...)
632 struct tab_joined_cell *j;
634 assert (table != NULL && text != NULL);
636 assert (x1 + table->col_ofs >= 0);
637 assert (y1 + table->row_ofs >= 0);
640 assert (y2 + table->row_ofs < table->nr);
641 assert (x2 + table->col_ofs < table->nc);
644 if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
645 || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
646 || x2 < x1 || x2 + table->col_ofs >= table->nc
647 || y2 < y2 || y2 + table->row_ofs >= table->nr)
649 printf ("tab_joint_text(): bad cell "
650 "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
651 x1, table->col_ofs, x1 + table->col_ofs,
652 y1, table->row_ofs, y1 + table->row_ofs,
653 x2, table->col_ofs, x2 + table->col_ofs,
654 y2, table->row_ofs, y2 + table->row_ofs,
655 table->nc, table->nr);
660 tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
662 j = pool_alloc (table->container, sizeof *j);
664 j->x1 = x1 + table->col_ofs;
665 j->y1 = y1 + table->row_ofs;
666 j->x2 = ++x2 + table->col_ofs;
667 j->y2 = ++y2 + table->row_ofs;
672 va_start (args, text);
673 j->contents = text_format (table, opt, text, args);
680 struct substring *cc = &table->cc[x1 + y1 * table->cf];
681 unsigned char *ct = &table->ct[x1 + y1 * table->cf];
682 const int ofs = table->cf - (x2 - x1);
686 for (y = y1; y < y2; y++)
690 for (x = x1; x < x2; x++)
692 *cc++ = ss_buffer ((char *) j, 0);
702 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
704 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
705 struct substring *string)
707 assert (table != NULL && string != NULL);
710 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
711 || c + table->col_ofs >= table->nc
712 || r + table->row_ofs >= table->nr)
714 printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
716 c, table->col_ofs, c + table->col_ofs,
717 r, table->row_ofs, r + table->row_ofs,
718 table->nc, table->nr);
723 table->cc[c + r * table->cf] = *string;
724 table->ct[c + r * table->cf] = opt;
729 /* Sets the widths of all the columns and heights of all the rows in
730 table T for driver D. */
732 nowrap_dim (struct tab_table *t, struct outp_driver *d)
734 t->w[0] = tab_natural_width (t, d, 0);
735 t->h[0] = d->font_height;
738 /* Sets the widths of all the columns and heights of all the rows in
739 table T for driver D. */
741 wrap_dim (struct tab_table *t, struct outp_driver *d)
743 t->w[0] = tab_natural_width (t, d, 0);
744 t->h[0] = tab_natural_height (t, d, 0);
747 /* Outputs text BUF as a table with a single cell having cell options
748 OPTIONS, which is a combination of the TAB_* and TAT_*
751 tab_output_text (int options, const char *buf, ...)
753 struct tab_table *t = tab_create (1, 1, 0);
754 char *tmp_buf = NULL;
756 if (options & TAT_PRINTF)
760 va_start (args, buf);
761 buf = tmp_buf = xvasprintf (buf, args);
765 tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
766 tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
767 tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim);
773 /* Set table flags to FLAGS. */
775 tab_flags (struct tab_table *t, unsigned flags)
781 /* Easy, type-safe way to submit a tab table to som. */
783 tab_submit (struct tab_table *t)
788 s.class = &tab_table_class;
797 /* Set table row and column offsets for all functions that affect
800 tab_offset (struct tab_table *t, int col, int row)
806 if (row < -1 || row > t->nr)
808 printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
811 if (col < -1 || col > t->nc)
813 printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
819 diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
821 diff += (col - t->col_ofs), t->col_ofs = col;
827 /* Increment the row offset by one. If the table is too small,
828 increase its size. */
830 tab_next_row (struct tab_table *t)
835 if (++t->row_ofs >= t->nr)
836 tab_realloc (t, -1, t->nr * 4 / 3);
839 static struct tab_table *t;
840 static struct outp_driver *d;
843 /* Set the current table to TABLE. */
845 tabi_table (struct som_entity *table)
847 assert (table != NULL);
848 assert (table->type == SOM_TABLE);
851 tab_offset (t, 0, 0);
853 assert (t->w == NULL && t->h == NULL);
854 t->w = pool_nalloc (t->container, t->nc, sizeof *t->w);
855 t->h = pool_nalloc (t->container, t->nr, sizeof *t->h);
856 t->hrh = pool_nmalloc (t->container, t->nr + 1, sizeof *t->hrh);
857 t->wrv = pool_nmalloc (t->container, t->nc + 1, sizeof *t->wrv);
860 /* Returns the line style to use for spacing purposes for a rule
861 of the given TYPE. */
862 static enum outp_line_style
863 rule_to_spacing_type (unsigned char type)
871 return OUTP_L_SINGLE;
873 return OUTP_L_DOUBLE;
879 /* Set the current output device to DRIVER. */
881 tabi_driver (struct outp_driver *driver)
886 assert (driver != NULL);
889 /* Figure out sizes of rules. */
890 for (r = 0; r <= t->nr; r++)
893 for (c = 0; c < t->nc; c++)
895 unsigned char rh = t->rh[c + r * t->cf];
896 int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
903 for (c = 0; c <= t->nc; c++)
906 for (r = 0; r < t->nr; r++)
908 unsigned char *rv = &t->rv[c + r * (t->cf + 1)];
910 if (*rv == UCHAR_MAX)
911 *rv = c != 0 && c != t->nc ? TAL_GAP : TAL_0;
912 w = driver->vert_line_width[rule_to_spacing_type (*rv)];
920 for (i = 0; i < t->nr; i++)
922 for (i = 0; i < t->nc; i++)
926 assert (t->dim != NULL);
933 for (i = 0; i < t->nr; i++)
937 printf ("Table row %d height not initialized.\n", i);
940 assert (t->h[i] > 0);
943 for (i = 0; i < t->nc; i++)
947 printf ("Table column %d width not initialized.\n", i);
950 assert (t->w[i] > 0);
955 /* Add up header sizes. */
956 for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
957 t->wl += t->w[i] + t->wrv[i + 1];
958 for (i = 0, t->ht = t->hrh[0]; i < t->t; i++)
959 t->ht += t->h[i] + t->hrh[i + 1];
960 for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++)
961 t->wr += t->w[i] + t->wrv[i + 1];
962 for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
963 t->hb += t->h[i] + t->hrh[i + 1];
966 if (!(t->flags & SOMF_NO_TITLE))
967 t->ht += d->font_height;
970 /* Return the number of columns and rows in the table into N_COLUMNS
971 and N_ROWS, respectively. */
973 tabi_count (int *n_columns, int *n_rows)
975 assert (n_columns != NULL && n_rows != NULL);
980 static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual);
982 /* Return the horizontal and vertical size of the entire table,
983 including headers, for the current output device, into HORIZ and
986 tabi_area (int *horiz, int *vert)
988 assert (horiz != NULL && vert != NULL);
993 for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
994 c < t->nc - t->r; c++)
995 w += t->w[c] + t->wrv[c];
1001 for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
1002 r < t->nr - t->b; r++)
1003 h += t->h[r] + t->hrh[r];
1008 /* Return the column style for this table into STYLE. */
1010 tabi_columns (int *style)
1012 assert (style != NULL);
1013 *style = t->col_style;
1016 /* Return the number of header rows/columns on the left, right, top,
1017 and bottom sides into HL, HR, HT, and HB, respectively. */
1019 tabi_headers (int *hl, int *hr, int *ht, int *hb)
1021 assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL);
1028 /* Determines the number of rows or columns (including appropriate
1029 headers), depending on CUMTYPE, that will fit into the space
1030 specified. Takes rows/columns starting at index START and attempts
1031 to fill up available space MAX. Returns in END the index of the
1032 last row/column plus one; returns in ACTUAL the actual amount of
1033 space the selected rows/columns (including appropriate headers)
1036 tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
1043 assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1044 if (cumtype == SOM_ROWS)
1046 assert (start >= 0 && start < t->nr);
1049 r = &t->hrh[start + 1];
1050 total = t->ht + t->hb;
1054 assert (start >= 0 && start < t->nc);
1057 r = &t->wrv[start + 1];
1058 total = t->wl + t->wr;
1074 for (x = start + 1; x < n; x++)
1076 int amt = *d++ + *r++;
1094 /* Return flags set for the current table into FLAGS. */
1096 tabi_flags (unsigned *flags)
1098 assert (flags != NULL);
1102 /* Returns true if the table will fit in the given page WIDTH,
1105 tabi_fits_width (int width)
1109 for (i = t->l; i < t->nc - t->r; i++)
1110 if (t->wl + t->wr + t->w[i] > width)
1116 /* Returns true if the table will fit in the given page LENGTH,
1119 tabi_fits_length (int length)
1123 for (i = t->t; i < t->nr - t->b; i++)
1124 if (t->ht + t->hb + t->h[i] > length)
1130 /* Sets the number of header rows/columns on the left, right, top,
1131 and bottom sides to HL, HR, HT, and HB, respectively. */
1133 tabi_set_headers (int hl, int hr, int ht, int hb)
1141 /* Render title for current table, with major index X and minor index
1142 Y. Y may be zero, or X and Y may be zero, but X should be nonzero
1145 tabi_title (int x, int y)
1150 if (t->flags & SOMF_NO_TITLE)
1153 cp = spprintf (buf, "%d.%d", table_num, subtable_num);
1155 cp = spprintf (cp, "(%d:%d)", x, y);
1157 cp = spprintf (cp, "(%d)", x);
1158 if (command_name != NULL)
1159 cp = spprintf (cp, " %s", command_name);
1160 cp = stpcpy (cp, ". ");
1161 if (t->title != NULL)
1163 size_t length = strlen (t->title);
1164 memcpy (cp, t->title, length);
1170 struct outp_text text;
1172 text.font = OUTP_PROPORTIONAL;
1173 text.justification = OUTP_LEFT;
1174 text.string = ss_buffer (buf, cp - buf);
1176 text.v = d->font_height;
1179 d->class->text_draw (d, &text);
1183 static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
1185 /* Renders columns C0...C1, plus headers, of rows R0...R1,
1186 at the given vertical position Y.
1187 C0 and C1 count vertical rules as columns,
1188 but R0 and R1 do not count horizontal rules as rows.
1189 Returns the vertical position after rendering. */
1191 render_rows (int y, int c0, int c1, int r0, int r1)
1194 for (r = r0; r < r1; r++)
1197 x = render_strip (x, y, r, 0, t->l * 2 + 1, r0, r1);
1198 x = render_strip (x, y, r, c0 * 2 + 1, c1 * 2, r0, r1);
1199 x = render_strip (x, y, r, (t->nc - t->r) * 2, t->nc * 2 + 1, r0, r1);
1200 y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2];
1205 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1206 current position on the current output device. */
1208 tabi_render (int c0, int r0, int c1, int r1)
1215 if (!(t->flags & SOMF_NO_TITLE))
1216 y += d->font_height;
1218 y = render_rows (y, c0, c1, 0, t->t * 2 + 1);
1219 y = render_rows (y, c0, c1, r0 * 2 + 1, r1 * 2);
1220 y = render_rows (y, c0, c1, (t->nr - t->b) * 2, t->nr * 2 + 1);
1223 const struct som_table_class tab_table_class =
1249 static enum outp_justification
1250 translate_justification (unsigned int opt)
1252 switch (opt & TAB_ALIGN_MASK)
1265 /* Returns the line style to use for drawing a rule of the given
1267 static enum outp_line_style
1268 rule_to_draw_type (unsigned char type)
1276 return OUTP_L_SINGLE;
1278 return OUTP_L_DOUBLE;
1284 /* Returns the horizontal rule at the given column and row. */
1286 get_hrule (int c, int r)
1288 return t->rh[c + r * t->cf];
1291 /* Returns the vertical rule at the given column and row. */
1293 get_vrule (int c, int r)
1295 return t->rv[c + r * (t->cf + 1)];
1298 /* Renders the horizontal rule at the given column and row
1299 at (X,Y) on the page. */
1301 render_horz_rule (int x, int y, int c, int r)
1303 enum outp_line_style style = rule_to_draw_type (get_hrule (c, r));
1304 if (style != OUTP_L_NONE)
1305 d->class->line (d, x, y, x + t->w[c], y + t->hrh[r],
1306 OUTP_L_NONE, style, OUTP_L_NONE, style);
1309 /* Renders the vertical rule at the given column and row
1310 at (X,Y) on the page. */
1312 render_vert_rule (int x, int y, int c, int r)
1314 enum outp_line_style style = rule_to_draw_type (get_vrule (c, r));
1315 if (style != OUTP_L_NONE)
1316 d->class->line (d, x, y, x + t->wrv[c], y + t->h[r],
1317 style, OUTP_L_NONE, style, OUTP_L_NONE);
1320 /* Renders the rule intersection at the given column and row
1321 at (X,Y) on the page. */
1323 render_rule_intersection (int x, int y, int c, int r)
1325 /* Bounds of intersection. */
1328 int x1 = x + t->wrv[c];
1329 int y1 = y + t->hrh[r];
1331 /* Lines on each side of intersection. */
1332 int top = r > 0 ? get_vrule (c, r - 1) : TAL_0;
1333 int left = c > 0 ? get_hrule (c - 1, r) : TAL_0;
1334 int bottom = r < t->nr ? get_vrule (c, r) : TAL_0;
1335 int right = c < t->nc ? get_hrule (c, r) : TAL_0;
1337 /* Output style for each line. */
1338 enum outp_line_style o_top = rule_to_draw_type (top);
1339 enum outp_line_style o_left = rule_to_draw_type (left);
1340 enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1341 enum outp_line_style o_right = rule_to_draw_type (right);
1343 if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1344 || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1345 d->class->line (d, x0, y0, x1, y1, o_top, o_left, o_bottom, o_right);
1348 /* Returns the width of columns C1...C2 exclusive,
1349 including interior but not exterior rules. */
1351 strip_width (int c1, int c2)
1356 for (c = c1; c < c2; c++)
1357 width += t->w[c] + t->wrv[c + 1];
1359 width -= t->wrv[c2];
1363 /* Returns the height of rows R1...R2 exclusive,
1364 including interior but not exterior rules. */
1366 strip_height (int r1, int r2)
1371 for (r = r1; r < r2; r++)
1372 height += t->h[r] + t->hrh[r + 1];
1374 height -= t->hrh[r2];
1378 /* Renders the cell at the given column and row at (X,Y) on the
1379 page. Also renders joined cells that extend as far to the
1380 right as C1 and as far down as R1. */
1382 render_cell (int x, int y, int c, int r, int c1, int r1)
1384 const int index = c + (r * t->cf);
1385 unsigned char type = t->ct[index];
1386 struct substring *content = &t->cc[index];
1388 if (!(type & TAB_JOIN))
1390 if (!(type & TAB_EMPTY))
1392 struct outp_text text;
1393 text.font = options_to_font (type);
1394 text.justification = translate_justification (type);
1395 text.string = *content;
1400 d->class->text_draw (d, &text);
1405 struct tab_joined_cell *j
1406 = (struct tab_joined_cell *) ss_data (*content);
1408 if (j->hit != tab_hit)
1412 if (j->x1 == c && j->y1 == r)
1414 struct outp_text text;
1415 text.font = options_to_font (type);
1416 text.justification = translate_justification (type);
1417 text.string = j->contents;
1420 text.h = strip_width (j->x1, MIN (j->x2, c1));
1421 text.v = strip_height (j->y1, MIN (j->y2, r1));
1422 d->class->text_draw (d, &text);
1428 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1429 on row R, at (X,Y). Returns X position after rendering.
1430 Also renders joined cells that extend beyond that strip,
1431 cropping them to lie within rendering region (C0,R0)-(C1,R1).
1432 C0 and C1 count vertical rules as columns.
1433 R counts horizontal rules as rows, but R0 and R1 do not. */
1435 render_strip (int x, int y, int r, int c0, int c1, int r0 UNUSED, int r1)
1439 for (c = c0; c < c1; c++)
1443 render_cell (x, y, c / 2, r / 2, c1 / 2, r1);
1445 render_horz_rule (x, y, c / 2, r / 2);
1451 render_vert_rule (x, y, c / 2, r / 2);
1453 render_rule_intersection (x, y, c / 2, r / 2);
1460 /* Sets COMMAND_NAME as the name of the current command,
1461 for embedding in output. */
1463 tab_set_command_name (const char *command_name_)
1465 free (command_name);
1466 command_name = command_name_ ? xstrdup (command_name_) : NULL;