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/compiler.h>
28 #include <data/format.h>
29 #include <libpspp/magic.h>
30 #include <libpspp/misc.h>
33 #include <libpspp/pool.h>
35 #include <data/variable.h>
38 #define _(msgid) gettext (msgid)
40 struct som_table_class tab_table_class;
41 static char *command_name;
43 /* Returns the font to use for a cell with the given OPTIONS. */
45 options_to_font (unsigned options)
47 return (options & TAB_FIX ? OUTP_FIXED
48 : options & TAB_EMPH ? OUTP_EMPHASIS
52 /* Creates a table with NC columns and NR rows. */
54 tab_create (int nc, int nr, int reallocable UNUSED)
58 t = pool_create_container (struct tab_table, container);
59 t->col_style = TAB_COL_NONE;
65 t->l = t->r = t->t = t->b = 0;
67 t->cc = pool_nmalloc (t->container, nr * nc, sizeof *t->cc);
68 t->ct = pool_malloc (t->container, nr * nc);
69 memset (t->ct, TAB_EMPTY, nc * nr);
71 t->rh = pool_nmalloc (t->container, nc, nr + 1);
72 memset (t->rh, 0, nc * (nr + 1));
74 t->rv = pool_nmalloc (t->container, nr, nc + 1);
75 memset (t->rv, UCHAR_MAX, nr * (nc + 1));
79 t->col_ofs = t->row_ofs = 0;
84 /* Destroys table T. */
86 tab_destroy (struct tab_table *t)
89 pool_destroy (t->container);
92 /* Sets the width and height of a table, in columns and rows,
93 respectively. Use only to reduce the size of a table, since it
94 does not change the amount of allocated memory. */
96 tab_resize (struct tab_table *t, int nc, int nr)
101 assert (nc + t->col_ofs <= t->cf);
102 t->nc = nc + t->col_ofs;
106 assert (nr + t->row_ofs <= t->nr);
107 t->nr = nr + t->row_ofs;
111 /* Changes either or both dimensions of a table. Consider using the
112 above routine instead if it won't waste a lot of space.
114 Changing the number of columns in a table is particularly expensive
115 in space and time. Avoid doing such. FIXME: In fact, transferring
116 of rules isn't even implemented yet. */
118 tab_realloc (struct tab_table *t, int nc, int nr)
126 tab_offset (t, 0, 0);
133 assert (nc == t->nc);
137 int mr1 = min (nr, t->nr);
138 int mc1 = min (nc, t->nc);
140 struct substring *new_cc;
141 unsigned char *new_ct;
144 new_cc = pool_nmalloc (t->container, nr * nc, sizeof *new_cc);
145 new_ct = pool_malloc (t->container, nr * nc);
146 for (r = 0; r < mr1; r++)
148 memcpy (&new_cc[r * nc], &t->cc[r * t->nc], mc1 * sizeof *t->cc);
149 memcpy (&new_ct[r * nc], &t->ct[r * t->nc], mc1);
150 memset (&new_ct[r * nc + t->nc], TAB_EMPTY, nc - t->nc);
152 pool_free (t->container, t->cc);
153 pool_free (t->container, t->ct);
158 else if (nr != t->nr)
160 t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc);
161 t->ct = pool_realloc (t->container, t->ct, nr * nc);
163 t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
164 t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
168 memset (&t->rh[nc * (t->nr + 1)], TAL_0, (nr - t->nr) * nc);
169 memset (&t->rv[(nc + 1) * t->nr], UCHAR_MAX,
170 (nr - t->nr) * (nc + 1));
174 memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr));
180 tab_offset (t, co, ro);
183 /* Sets the number of header rows on each side of TABLE to L on the
184 left, R on the right, T on the top, B on the bottom. Header rows
185 are repeated when a table is broken across multiple columns or
188 tab_headers (struct tab_table *table, int l, int r, int t, int b)
190 assert (table != NULL);
191 assert (l < table->nc);
192 assert (r < table->nc);
193 assert (t < table->nr);
194 assert (b < table->nr);
203 /* Set up table T so that, when it is an appropriate size, it will be
204 displayed across the page in columns.
206 STYLE is a TAB_COL_* constant. GROUP is the number of rows to take
209 tab_columns (struct tab_table *t, int style, int group)
212 t->col_style = style;
213 t->col_group = group;
218 /* Draws a vertical line to the left of cells at horizontal position X
219 from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
221 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
226 if (x + t->col_ofs < 0 || x + t->col_ofs > t->nc
227 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
228 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
230 printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
231 "table size (%d,%d)\n"),
232 x, t->col_ofs, x + t->col_ofs,
233 y1, t->row_ofs, y1 + t->row_ofs,
234 y2, t->row_ofs, y2 + t->row_ofs,
248 assert (y2 <= t->nr);
253 for (y = y1; y <= y2; y++)
254 t->rv[x + (t->cf + 1) * y] = style;
258 /* Draws a horizontal line above cells at vertical position Y from X1
259 to X2 inclusive in style STYLE, if style is not -1. */
261 tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
278 for (x = x1; x <= x2; x++)
279 t->rh[x + t->cf * y] = style;
283 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
284 lines of style F_H and vertical lines of style F_V. Fills the
285 interior of the box with horizontal lines of style I_H and vertical
286 lines of style I_V. Any of the line styles may be -1 to avoid
287 drawing those lines. This is distinct from 0, which draws a null
290 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
291 int x1, int y1, int x2, int y2)
296 if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc
297 || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= t->nc
298 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
299 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
301 printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
302 "in table size (%d,%d)\n"),
303 x1, t->col_ofs, x1 + t->col_ofs,
304 y1, t->row_ofs, y1 + t->row_ofs,
305 x2, t->col_ofs, x2 + t->col_ofs,
306 y2, t->row_ofs, y2 + t->row_ofs,
327 for (x = x1; x <= x2; x++)
329 t->rh[x + t->cf * y1] = f_h;
330 t->rh[x + t->cf * (y2 + 1)] = f_h;
336 for (y = y1; y <= y2; y++)
338 t->rv[x1 + (t->cf + 1) * y] = f_v;
339 t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
347 for (y = y1 + 1; y <= y2; y++)
351 for (x = x1; x <= x2; x++)
352 t->rh[x + t->cf * y] = i_h;
359 for (x = x1 + 1; x <= x2; x++)
363 for (y = y1; y <= y2; y++)
364 t->rv[x + (t->cf + 1) * y] = i_v;
369 /* Formats text TEXT and arguments ARGS as indicated in OPT in
370 TABLE's pool and returns the resultant string. */
371 static struct substring
372 text_format (struct tab_table *table, int opt, const char *text, va_list args)
374 assert (table != NULL && text != NULL);
376 return ss_cstr (opt & TAT_PRINTF
377 ? pool_vasprintf (table->container, text, args)
378 : pool_strdup (table->container, text));
381 /* Set the title of table T to TITLE, which is formatted as if
382 passed to printf(). */
384 tab_title (struct tab_table *t, const char *title, ...)
388 assert (t != NULL && title != NULL);
389 va_start (args, title);
390 t->title = xvasprintf (title, args);
394 /* Set DIM_FUNC as the dimension function for table T. */
396 tab_dim (struct tab_table *t, tab_dim_func *dim_func)
398 assert (t != NULL && t->dim == NULL);
402 /* Returns the natural width of column C in table T for driver D, that
403 is, the smallest width necessary to display all its cells without
404 wrapping. The width will be no larger than the page width minus
405 left and right rule widths. */
407 tab_natural_width (struct tab_table *t, struct outp_driver *d, int c)
411 assert (t != NULL && c >= 0 && c < t->nc);
415 for (width = r = 0; r < t->nr; r++)
417 struct outp_text text;
418 unsigned char opt = t->ct[c + r * t->cf];
421 if (opt & (TAB_JOIN | TAB_EMPTY))
424 text.string = t->cc[c + r * t->cf];
425 text.justification = OUTP_LEFT;
426 text.font = options_to_font (opt);
427 text.h = text.v = INT_MAX;
429 d->class->text_metrics (d, &text, &w, NULL);
437 width = d->prop_em_width * 8;
439 printf ("warning: table column %d contains no data.\n", c);
444 const int clamp = d->width - t->wrv[0] - t->wrv[t->nc];
453 /* Returns the natural height of row R in table T for driver D, that
454 is, the minimum height necessary to display the information in the
455 cell at the widths set for each column. */
457 tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
461 assert (t != NULL && r >= 0 && r < t->nr);
466 for (height = d->font_height, c = 0; c < t->nc; c++)
468 struct outp_text text;
469 unsigned char opt = t->ct[c + r * t->cf];
472 assert (t->w[c] != NOT_INT);
473 if (opt & (TAB_JOIN | TAB_EMPTY))
476 text.string = t->cc[c + r * t->cf];
477 text.justification = OUTP_LEFT;
478 text.font = options_to_font (opt);
481 d->class->text_metrics (d, &text, NULL, &h);
491 /* Callback function to set all columns and rows to their natural
492 dimensions. Not really meant to be called directly. */
494 tab_natural_dimensions (struct tab_table *t, struct outp_driver *d)
500 for (i = 0; i < t->nc; i++)
501 t->w[i] = tab_natural_width (t, d, i);
503 for (i = 0; i < t->nr; i++)
504 t->h[i] = tab_natural_height (t, d, i);
510 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
511 from V, displayed with format spec F. */
513 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
514 const union value *v, const struct fmt_spec *f)
518 assert (table != NULL && v != NULL && f != NULL);
520 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
521 || c + table->col_ofs >= table->nc
522 || r + table->row_ofs >= table->nr)
524 printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
526 c, table->col_ofs, c + table->col_ofs,
527 r, table->row_ofs, r + table->row_ofs,
528 table->nc, table->nr);
533 contents = pool_alloc (table->container, f->w);
534 table->cc[c + r * table->cf] = ss_buffer (contents, f->w);
535 table->ct[c + r * table->cf] = opt;
537 data_out (contents, f, v);
540 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
541 with NDEC decimal places. */
543 tab_float (struct tab_table *table, int c, int r, unsigned char opt,
544 double val, int w, int d)
550 union value double_value;
552 assert (table != NULL && w <= 40);
555 assert (c < table->nc);
557 assert (r < table->nr);
559 f = make_output_format (FMT_F, w, d);
562 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
563 || c + table->col_ofs >= table->nc
564 || r + table->row_ofs >= table->nr)
566 printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
568 c, table->col_ofs, c + table->col_ofs,
569 r, table->row_ofs, r + table->row_ofs,
570 table->nc, table->nr);
575 double_value.f = val;
576 data_out (buf, &f, &double_value);
579 while (isspace ((unsigned char) *cp) && cp < &buf[w])
581 f.w = w - (cp - buf);
583 contents = pool_alloc (table->container, f.w);
584 table->cc[c + r * table->cf] = ss_buffer (contents, f.w);
585 table->ct[c + r * table->cf] = opt;
586 memcpy (contents, cp, f.w);
589 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
592 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
596 assert (table != NULL && text != NULL);
600 assert (c < table->nc);
601 assert (r < table->nr);
605 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
606 || c + table->col_ofs >= table->nc
607 || r + table->row_ofs >= table->nr)
609 printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
611 c, table->col_ofs, c + table->col_ofs,
612 r, table->row_ofs, r + table->row_ofs,
613 table->nc, table->nr);
618 va_start (args, text);
619 table->cc[c + r * table->cf] = text_format (table, opt, text, args);
620 table->ct[c + r * table->cf] = opt;
624 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
625 options OPT to have text value TEXT. */
627 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
628 unsigned opt, const char *text, ...)
630 struct tab_joined_cell *j;
632 assert (table != NULL && text != NULL);
634 assert (x1 + table->col_ofs >= 0);
635 assert (y1 + table->row_ofs >= 0);
638 assert (y2 + table->row_ofs < table->nr);
639 assert (x2 + table->col_ofs < table->nc);
642 if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
643 || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
644 || x2 < x1 || x2 + table->col_ofs >= table->nc
645 || y2 < y2 || y2 + table->row_ofs >= table->nr)
647 printf ("tab_joint_text(): bad cell "
648 "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
649 x1, table->col_ofs, x1 + table->col_ofs,
650 y1, table->row_ofs, y1 + table->row_ofs,
651 x2, table->col_ofs, x2 + table->col_ofs,
652 y2, table->row_ofs, y2 + table->row_ofs,
653 table->nc, table->nr);
658 tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
660 j = pool_alloc (table->container, sizeof *j);
662 j->x1 = x1 + table->col_ofs;
663 j->y1 = y1 + table->row_ofs;
664 j->x2 = ++x2 + table->col_ofs;
665 j->y2 = ++y2 + table->row_ofs;
670 va_start (args, text);
671 j->contents = text_format (table, opt, text, args);
678 struct substring *cc = &table->cc[x1 + y1 * table->cf];
679 unsigned char *ct = &table->ct[x1 + y1 * table->cf];
680 const int ofs = table->cf - (x2 - x1);
684 for (y = y1; y < y2; y++)
688 for (x = x1; x < x2; x++)
690 *cc++ = ss_buffer ((char *) j, 0);
700 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
702 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
703 struct substring *string)
705 assert (table != NULL && string != NULL);
708 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
709 || c + table->col_ofs >= table->nc
710 || r + table->row_ofs >= table->nr)
712 printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
714 c, table->col_ofs, c + table->col_ofs,
715 r, table->row_ofs, r + table->row_ofs,
716 table->nc, table->nr);
721 table->cc[c + r * table->cf] = *string;
722 table->ct[c + r * table->cf] = opt;
727 /* Sets the widths of all the columns and heights of all the rows in
728 table T for driver D. */
730 nowrap_dim (struct tab_table *t, struct outp_driver *d)
732 t->w[0] = tab_natural_width (t, d, 0);
733 t->h[0] = d->font_height;
736 /* Sets the widths of all the columns and heights of all the rows in
737 table T for driver D. */
739 wrap_dim (struct tab_table *t, struct outp_driver *d)
741 t->w[0] = tab_natural_width (t, d, 0);
742 t->h[0] = tab_natural_height (t, d, 0);
745 /* Outputs text BUF as a table with a single cell having cell options
746 OPTIONS, which is a combination of the TAB_* and TAT_*
749 tab_output_text (int options, const char *buf, ...)
751 struct tab_table *t = tab_create (1, 1, 0);
752 char *tmp_buf = NULL;
754 if (options & TAT_PRINTF)
758 va_start (args, buf);
759 buf = tmp_buf = xvasprintf (buf, args);
763 tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
764 tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
765 tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim);
771 /* Set table flags to FLAGS. */
773 tab_flags (struct tab_table *t, unsigned flags)
779 /* Easy, type-safe way to submit a tab table to som. */
781 tab_submit (struct tab_table *t)
786 s.class = &tab_table_class;
795 /* Set table row and column offsets for all functions that affect
798 tab_offset (struct tab_table *t, int col, int row)
804 if (row < -1 || row >= t->nr)
806 printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
809 if (col < -1 || col >= t->nc)
811 printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
817 diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
819 diff += (col - t->col_ofs), t->col_ofs = col;
825 /* Increment the row offset by one. If the table is too small,
826 increase its size. */
828 tab_next_row (struct tab_table *t)
833 if (++t->row_ofs >= t->nr)
834 tab_realloc (t, -1, t->nr * 4 / 3);
837 static struct tab_table *t;
838 static struct outp_driver *d;
841 /* Set the current table to TABLE. */
843 tabi_table (struct som_entity *table)
845 assert (table != NULL);
846 assert (table->type == SOM_TABLE);
849 tab_offset (t, 0, 0);
851 assert (t->w == NULL && t->h == NULL);
852 t->w = pool_nalloc (t->container, t->nc, sizeof *t->w);
853 t->h = pool_nalloc (t->container, t->nr, sizeof *t->h);
854 t->hrh = pool_nmalloc (t->container, t->nr + 1, sizeof *t->hrh);
855 t->wrv = pool_nmalloc (t->container, t->nc + 1, sizeof *t->wrv);
858 /* Returns the line style to use for spacing purposes for a rule
859 of the given TYPE. */
860 static enum outp_line_style
861 rule_to_spacing_type (unsigned char type)
869 return OUTP_L_SINGLE;
871 return OUTP_L_DOUBLE;
877 /* Set the current output device to DRIVER. */
879 tabi_driver (struct outp_driver *driver)
884 assert (driver != NULL);
887 /* Figure out sizes of rules. */
888 for (r = 0; r <= t->nr; r++)
891 for (c = 0; c < t->nc; c++)
893 unsigned char rh = t->rh[c + r * t->cf];
894 int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
901 for (c = 0; c <= t->nc; c++)
904 for (r = 0; r < t->nr; r++)
906 unsigned char *rv = &t->rv[c + r * (t->cf + 1)];
908 if (*rv == UCHAR_MAX)
909 *rv = c != 0 && c != t->nc ? TAL_GAP : TAL_0;
910 w = driver->vert_line_width[rule_to_spacing_type (*rv)];
918 for (i = 0; i < t->nr; i++)
920 for (i = 0; i < t->nc; i++)
924 assert (t->dim != NULL);
931 for (i = 0; i < t->nr; i++)
935 printf ("Table row %d height not initialized.\n", i);
938 assert (t->h[i] > 0);
941 for (i = 0; i < t->nc; i++)
945 printf ("Table column %d width not initialized.\n", i);
948 assert (t->w[i] > 0);
953 /* Add up header sizes. */
954 for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
955 t->wl += t->w[i] + t->wrv[i + 1];
956 for (i = 0, t->ht = t->hrh[0]; i < t->t; i++)
957 t->ht += t->h[i] + t->hrh[i + 1];
958 for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++)
959 t->wr += t->w[i] + t->wrv[i + 1];
960 for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
961 t->hb += t->h[i] + t->hrh[i + 1];
964 if (!(t->flags & SOMF_NO_TITLE))
965 t->ht += d->font_height;
968 /* Return the number of columns and rows in the table into N_COLUMNS
969 and N_ROWS, respectively. */
971 tabi_count (int *n_columns, int *n_rows)
973 assert (n_columns != NULL && n_rows != NULL);
978 static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual);
980 /* Return the horizontal and vertical size of the entire table,
981 including headers, for the current output device, into HORIZ and
984 tabi_area (int *horiz, int *vert)
986 assert (horiz != NULL && vert != NULL);
991 for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
992 c < t->nc - t->r; c++)
993 w += t->w[c] + t->wrv[c];
999 for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
1000 r < t->nr - t->b; r++)
1001 h += t->h[r] + t->hrh[r];
1006 /* Return the column style for this table into STYLE. */
1008 tabi_columns (int *style)
1010 assert (style != NULL);
1011 *style = t->col_style;
1014 /* Return the number of header rows/columns on the left, right, top,
1015 and bottom sides into HL, HR, HT, and HB, respectively. */
1017 tabi_headers (int *hl, int *hr, int *ht, int *hb)
1019 assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL);
1026 /* Determines the number of rows or columns (including appropriate
1027 headers), depending on CUMTYPE, that will fit into the space
1028 specified. Takes rows/columns starting at index START and attempts
1029 to fill up available space MAX. Returns in END the index of the
1030 last row/column plus one; returns in ACTUAL the actual amount of
1031 space the selected rows/columns (including appropriate headers)
1034 tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
1041 assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1042 if (cumtype == SOM_ROWS)
1044 assert (start >= 0 && start < t->nr);
1047 r = &t->hrh[start + 1];
1048 total = t->ht + t->hb;
1052 assert (start >= 0 && start < t->nc);
1055 r = &t->wrv[start + 1];
1056 total = t->wl + t->wr;
1072 for (x = start + 1; x < n; x++)
1074 int amt = *d++ + *r++;
1092 /* Return flags set for the current table into FLAGS. */
1094 tabi_flags (unsigned *flags)
1096 assert (flags != NULL);
1100 /* Returns true if the table will fit in the given page WIDTH,
1103 tabi_fits_width (int width)
1107 for (i = t->l; i < t->nc - t->r; i++)
1108 if (t->wl + t->wr + t->w[i] > width)
1114 /* Returns true if the table will fit in the given page LENGTH,
1117 tabi_fits_length (int length)
1121 for (i = t->t; i < t->nr - t->b; i++)
1122 if (t->ht + t->hb + t->h[i] > length)
1128 /* Sets the number of header rows/columns on the left, right, top,
1129 and bottom sides to HL, HR, HT, and HB, respectively. */
1131 tabi_set_headers (int hl, int hr, int ht, int hb)
1139 /* Render title for current table, with major index X and minor index
1140 Y. Y may be zero, or X and Y may be zero, but X should be nonzero
1143 tabi_title (int x, int y)
1148 if (t->flags & SOMF_NO_TITLE)
1151 cp = spprintf (buf, "%d.%d", table_num, subtable_num);
1153 cp = spprintf (cp, "(%d:%d)", x, y);
1155 cp = spprintf (cp, "(%d)", x);
1156 if (command_name != NULL)
1157 cp = spprintf (cp, " %s", command_name);
1158 cp = stpcpy (cp, ". ");
1159 if (t->title != NULL)
1161 size_t length = strlen (t->title);
1162 memcpy (cp, t->title, length);
1168 struct outp_text text;
1170 text.font = OUTP_PROPORTIONAL;
1171 text.justification = OUTP_LEFT;
1172 text.string = ss_buffer (buf, cp - buf);
1174 text.v = d->font_height;
1177 d->class->text_draw (d, &text);
1181 static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
1183 /* Renders columns C0...C1, plus headers, of rows R0...R1,
1184 at the given vertical position Y.
1185 C0 and C1 count vertical rules as columns,
1186 but R0 and R1 do not count horizontal rules as rows.
1187 Returns the vertical position after rendering. */
1189 render_rows (int y, int c0, int c1, int r0, int r1)
1192 for (r = r0; r < r1; r++)
1195 x = render_strip (x, y, r, 0, t->l * 2 + 1, r0, r1);
1196 x = render_strip (x, y, r, c0 * 2 + 1, c1 * 2, r0, r1);
1197 x = render_strip (x, y, r, (t->nc - t->r) * 2, t->nc * 2 + 1, r0, r1);
1198 y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2];
1203 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1204 current position on the current output device. */
1206 tabi_render (int c0, int r0, int c1, int r1)
1213 if (!(t->flags & SOMF_NO_TITLE))
1214 y += d->font_height;
1216 y = render_rows (y, c0, c1, 0, t->t * 2 + 1);
1217 y = render_rows (y, c0, c1, r0 * 2 + 1, r1 * 2);
1218 y = render_rows (y, c0, c1, (t->nr - t->b) * 2, t->nr * 2 + 1);
1221 struct som_table_class tab_table_class =
1247 static enum outp_justification
1248 translate_justification (unsigned int opt)
1250 switch (opt & TAB_ALIGN_MASK)
1263 /* Returns the line style to use for drawing a rule of the given
1265 static enum outp_line_style
1266 rule_to_draw_type (unsigned char type)
1274 return OUTP_L_SINGLE;
1276 return OUTP_L_DOUBLE;
1282 /* Returns the horizontal rule at the given column and row. */
1284 get_hrule (int c, int r)
1286 return t->rh[c + r * t->cf];
1289 /* Returns the vertical rule at the given column and row. */
1291 get_vrule (int c, int r)
1293 return t->rv[c + r * (t->cf + 1)];
1296 /* Renders the horizontal rule at the given column and row
1297 at (X,Y) on the page. */
1299 render_horz_rule (int x, int y, int c, int r)
1301 enum outp_line_style style = rule_to_draw_type (get_hrule (c, r));
1302 if (style != OUTP_L_NONE)
1303 d->class->line (d, x, y, x + t->w[c], y + t->hrh[r],
1304 OUTP_L_NONE, style, OUTP_L_NONE, style);
1307 /* Renders the vertical rule at the given column and row
1308 at (X,Y) on the page. */
1310 render_vert_rule (int x, int y, int c, int r)
1312 enum outp_line_style style = rule_to_draw_type (get_vrule (c, r));
1313 if (style != OUTP_L_NONE)
1314 d->class->line (d, x, y, x + t->wrv[c], y + t->h[r],
1315 style, OUTP_L_NONE, style, OUTP_L_NONE);
1318 /* Renders the rule intersection at the given column and row
1319 at (X,Y) on the page. */
1321 render_rule_intersection (int x, int y, int c, int r)
1323 /* Bounds of intersection. */
1326 int x1 = x + t->wrv[c];
1327 int y1 = y + t->hrh[r];
1329 /* Lines on each side of intersection. */
1330 int top = r > 0 ? get_vrule (c, r - 1) : TAL_0;
1331 int left = c > 0 ? get_hrule (c - 1, r) : TAL_0;
1332 int bottom = r < t->nr ? get_vrule (c, r) : TAL_0;
1333 int right = c < t->nc ? get_hrule (c, r) : TAL_0;
1335 /* Output style for each line. */
1336 enum outp_line_style o_top = rule_to_draw_type (top);
1337 enum outp_line_style o_left = rule_to_draw_type (left);
1338 enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1339 enum outp_line_style o_right = rule_to_draw_type (right);
1341 if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1342 || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1343 d->class->line (d, x0, y0, x1, y1, o_top, o_left, o_bottom, o_right);
1346 /* Returns the width of columns C1...C2 exclusive,
1347 including interior but not exterior rules. */
1349 strip_width (int c1, int c2)
1354 for (c = c1; c < c2; c++)
1355 width += t->w[c] + t->wrv[c + 1];
1357 width -= t->wrv[c2];
1361 /* Returns the height of rows R1...R2 exclusive,
1362 including interior but not exterior rules. */
1364 strip_height (int r1, int r2)
1369 for (r = r1; r < r2; r++)
1370 height += t->h[r] + t->hrh[r + 1];
1372 height -= t->hrh[r2];
1376 /* Renders the cell at the given column and row at (X,Y) on the
1377 page. Also renders joined cells that extend as far to the
1378 right as C1 and as far down as R1. */
1380 render_cell (int x, int y, int c, int r, int c1, int r1)
1382 const int index = c + (r * t->cf);
1383 unsigned char type = t->ct[index];
1384 struct substring *content = &t->cc[index];
1386 if (!(type & TAB_JOIN))
1388 if (!(type & TAB_EMPTY))
1390 struct outp_text text;
1391 text.font = options_to_font (type);
1392 text.justification = translate_justification (type);
1393 text.string = *content;
1398 d->class->text_draw (d, &text);
1403 struct tab_joined_cell *j
1404 = (struct tab_joined_cell *) ss_data (*content);
1406 if (j->hit != tab_hit)
1410 if (j->x1 == c && j->y1 == r)
1412 struct outp_text text;
1413 text.font = options_to_font (type);
1414 text.justification = translate_justification (type);
1415 text.string = j->contents;
1418 text.h = strip_width (j->x1, MIN (j->x2, c1));
1419 text.v = strip_height (j->y1, MIN (j->y2, r1));
1420 d->class->text_draw (d, &text);
1426 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1427 on row R, at (X,Y). Returns X position after rendering.
1428 Also renders joined cells that extend beyond that strip,
1429 cropping them to lie within rendering region (C0,R0)-(C1,R1).
1430 C0 and C1 count vertical rules as columns.
1431 R counts horizontal rules as rows, but R0 and R1 do not. */
1433 render_strip (int x, int y, int r, int c0, int c1, int r0 UNUSED, int r1)
1437 for (c = c0; c < c1; c++)
1441 render_cell (x, y, c / 2, r / 2, c1 / 2, r1);
1443 render_horz_rule (x, y, c / 2, r / 2);
1449 render_vert_rule (x, y, c / 2, r / 2);
1451 render_rule_intersection (x, y, c / 2, r / 2);
1458 /* Sets COMMAND_NAME as the name of the current command,
1459 for embedding in output. */
1461 tab_set_command_name (const char *command_name_)
1463 free (command_name);
1464 command_name = command_name_ ? xstrdup (command_name_) : NULL;