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 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)
90 pool_destroy (t->container);
93 /* Sets the width and height of a table, in columns and rows,
94 respectively. Use only to reduce the size of a table, since it
95 does not change the amount of allocated memory. */
97 tab_resize (struct tab_table *t, int nc, int nr)
102 assert (nc + t->col_ofs <= t->cf);
103 t->nc = nc + t->col_ofs;
107 assert (nr + t->row_ofs <= t->nr);
108 t->nr = nr + t->row_ofs;
112 /* Changes either or both dimensions of a table. Consider using the
113 above routine instead if it won't waste a lot of space.
115 Changing the number of columns in a table is particularly expensive
116 in space and time. Avoid doing such. FIXME: In fact, transferring
117 of rules isn't even implemented yet. */
119 tab_realloc (struct tab_table *t, int nc, int nr)
127 tab_offset (t, 0, 0);
134 assert (nc == t->nc);
138 int mr1 = min (nr, t->nr);
139 int mc1 = min (nc, t->nc);
141 struct substring *new_cc;
142 unsigned char *new_ct;
145 new_cc = pool_nmalloc (t->container, nr * nc, sizeof *new_cc);
146 new_ct = pool_malloc (t->container, nr * nc);
147 for (r = 0; r < mr1; r++)
149 memcpy (&new_cc[r * nc], &t->cc[r * t->nc], mc1 * sizeof *t->cc);
150 memcpy (&new_ct[r * nc], &t->ct[r * t->nc], mc1);
151 memset (&new_ct[r * nc + t->nc], TAB_EMPTY, nc - t->nc);
153 pool_free (t->container, t->cc);
154 pool_free (t->container, t->ct);
159 else if (nr != t->nr)
161 t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc);
162 t->ct = pool_realloc (t->container, t->ct, nr * nc);
164 t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
165 t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
169 memset (&t->rh[nc * (t->nr + 1)], TAL_0, (nr - t->nr) * nc);
170 memset (&t->rv[(nc + 1) * t->nr], UCHAR_MAX,
171 (nr - t->nr) * (nc + 1));
175 memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr));
181 tab_offset (t, co, ro);
184 /* Sets the number of header rows on each side of TABLE to L on the
185 left, R on the right, T on the top, B on the bottom. Header rows
186 are repeated when a table is broken across multiple columns or
189 tab_headers (struct tab_table *table, int l, int r, int t, int b)
191 assert (table != NULL);
192 assert (l < table->nc);
193 assert (r < table->nc);
194 assert (t < table->nr);
195 assert (b < table->nr);
204 /* Set up table T so that, when it is an appropriate size, it will be
205 displayed across the page in columns.
207 STYLE is a TAB_COL_* constant. GROUP is the number of rows to take
210 tab_columns (struct tab_table *t, int style, int group)
213 t->col_style = style;
214 t->col_group = group;
219 /* Draws a vertical line to the left of cells at horizontal position X
220 from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
222 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
227 if (x + t->col_ofs < 0 || x + t->col_ofs > t->nc
228 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
229 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
231 printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
232 "table size (%d,%d)\n"),
233 x, t->col_ofs, x + t->col_ofs,
234 y1, t->row_ofs, y1 + t->row_ofs,
235 y2, t->row_ofs, y2 + t->row_ofs,
249 assert (y2 <= t->nr);
254 for (y = y1; y <= y2; y++)
255 t->rv[x + (t->cf + 1) * y] = style;
259 /* Draws a horizontal line above cells at vertical position Y from X1
260 to X2 inclusive in style STYLE, if style is not -1. */
262 tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
279 for (x = x1; x <= x2; x++)
280 t->rh[x + t->cf * y] = style;
284 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
285 lines of style F_H and vertical lines of style F_V. Fills the
286 interior of the box with horizontal lines of style I_H and vertical
287 lines of style I_V. Any of the line styles may be -1 to avoid
288 drawing those lines. This is distinct from 0, which draws a null
291 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
292 int x1, int y1, int x2, int y2)
297 if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc
298 || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= t->nc
299 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
300 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
302 printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
303 "in table size (%d,%d)\n"),
304 x1, t->col_ofs, x1 + t->col_ofs,
305 y1, t->row_ofs, y1 + t->row_ofs,
306 x2, t->col_ofs, x2 + t->col_ofs,
307 y2, t->row_ofs, y2 + t->row_ofs,
328 for (x = x1; x <= x2; x++)
330 t->rh[x + t->cf * y1] = f_h;
331 t->rh[x + t->cf * (y2 + 1)] = f_h;
337 for (y = y1; y <= y2; y++)
339 t->rv[x1 + (t->cf + 1) * y] = f_v;
340 t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
348 for (y = y1 + 1; y <= y2; y++)
352 for (x = x1; x <= x2; x++)
353 t->rh[x + t->cf * y] = i_h;
360 for (x = x1 + 1; x <= x2; x++)
364 for (y = y1; y <= y2; y++)
365 t->rv[x + (t->cf + 1) * y] = i_v;
370 /* Formats text TEXT and arguments ARGS as indicated in OPT in
371 TABLE's pool and returns the resultant string. */
372 static struct substring
373 text_format (struct tab_table *table, int opt, const char *text, va_list args)
375 assert (table != NULL && text != NULL);
377 return ss_cstr (opt & TAT_PRINTF
378 ? pool_vasprintf (table->container, text, args)
379 : pool_strdup (table->container, text));
382 /* Set the title of table T to TITLE, which is formatted as if
383 passed to printf(). */
385 tab_title (struct tab_table *t, const char *title, ...)
389 assert (t != NULL && title != NULL);
390 va_start (args, title);
391 t->title = xvasprintf (title, args);
395 /* Set DIM_FUNC as the dimension function for table T. */
397 tab_dim (struct tab_table *t, tab_dim_func *dim_func)
399 assert (t != NULL && t->dim == NULL);
403 /* Returns the natural width of column C in table T for driver D, that
404 is, the smallest width necessary to display all its cells without
405 wrapping. The width will be no larger than the page width minus
406 left and right rule widths. */
408 tab_natural_width (struct tab_table *t, struct outp_driver *d, int c)
412 assert (t != NULL && c >= 0 && c < t->nc);
416 for (width = r = 0; r < t->nr; r++)
418 struct outp_text text;
419 unsigned char opt = t->ct[c + r * t->cf];
422 if (opt & (TAB_JOIN | TAB_EMPTY))
425 text.string = t->cc[c + r * t->cf];
426 text.justification = OUTP_LEFT;
427 text.font = options_to_font (opt);
428 text.h = text.v = INT_MAX;
430 d->class->text_metrics (d, &text, &w, NULL);
438 width = d->prop_em_width * 8;
440 printf ("warning: table column %d contains no data.\n", c);
445 const int clamp = d->width - t->wrv[0] - t->wrv[t->nc];
454 /* Returns the natural height of row R in table T for driver D, that
455 is, the minimum height necessary to display the information in the
456 cell at the widths set for each column. */
458 tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
462 assert (t != NULL && r >= 0 && r < t->nr);
467 for (height = d->font_height, c = 0; c < t->nc; c++)
469 struct outp_text text;
470 unsigned char opt = t->ct[c + r * t->cf];
473 assert (t->w[c] != NOT_INT);
474 if (opt & (TAB_JOIN | TAB_EMPTY))
477 text.string = t->cc[c + r * t->cf];
478 text.justification = OUTP_LEFT;
479 text.font = options_to_font (opt);
482 d->class->text_metrics (d, &text, NULL, &h);
492 /* Callback function to set all columns and rows to their natural
493 dimensions. Not really meant to be called directly. */
495 tab_natural_dimensions (struct tab_table *t, struct outp_driver *d)
501 for (i = 0; i < t->nc; i++)
502 t->w[i] = tab_natural_width (t, d, i);
504 for (i = 0; i < t->nr; i++)
505 t->h[i] = tab_natural_height (t, d, i);
511 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
512 from V, displayed with format spec F. */
514 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
515 const union value *v, const struct fmt_spec *f)
519 assert (table != NULL && v != NULL && f != NULL);
521 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
522 || c + table->col_ofs >= table->nc
523 || r + table->row_ofs >= table->nr)
525 printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
527 c, table->col_ofs, c + table->col_ofs,
528 r, table->row_ofs, r + table->row_ofs,
529 table->nc, table->nr);
534 contents = pool_alloc (table->container, f->w);
535 table->cc[c + r * table->cf] = ss_buffer (contents, f->w);
536 table->ct[c + r * table->cf] = opt;
538 data_out (contents, f, v);
541 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
542 with NDEC decimal places. */
544 tab_float (struct tab_table *table, int c, int r, unsigned char opt,
545 double val, int w, int d)
551 union value double_value;
553 assert (table != NULL && w <= 40);
556 assert (c < table->nc);
558 assert (r < table->nr);
560 f = make_output_format (FMT_F, w, d);
563 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
564 || c + table->col_ofs >= table->nc
565 || r + table->row_ofs >= table->nr)
567 printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
569 c, table->col_ofs, c + table->col_ofs,
570 r, table->row_ofs, r + table->row_ofs,
571 table->nc, table->nr);
576 double_value.f = val;
577 data_out (buf, &f, &double_value);
580 while (isspace ((unsigned char) *cp) && cp < &buf[w])
582 f.w = w - (cp - buf);
584 contents = pool_alloc (table->container, f.w);
585 table->cc[c + r * table->cf] = ss_buffer (contents, f.w);
586 table->ct[c + r * table->cf] = opt;
587 memcpy (contents, cp, f.w);
590 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
593 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
597 assert (table != NULL && text != NULL);
601 assert (c < table->nc);
602 assert (r < table->nr);
606 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
607 || c + table->col_ofs >= table->nc
608 || r + table->row_ofs >= table->nr)
610 printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
612 c, table->col_ofs, c + table->col_ofs,
613 r, table->row_ofs, r + table->row_ofs,
614 table->nc, table->nr);
619 va_start (args, text);
620 table->cc[c + r * table->cf] = text_format (table, opt, text, args);
621 table->ct[c + r * table->cf] = opt;
625 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
626 options OPT to have text value TEXT. */
628 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
629 unsigned opt, const char *text, ...)
631 struct tab_joined_cell *j;
633 assert (table != NULL && text != NULL);
635 assert (x1 + table->col_ofs >= 0);
636 assert (y1 + table->row_ofs >= 0);
639 assert (y2 + table->row_ofs < table->nr);
640 assert (x2 + table->col_ofs < table->nc);
643 if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
644 || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
645 || x2 < x1 || x2 + table->col_ofs >= table->nc
646 || y2 < y2 || y2 + table->row_ofs >= table->nr)
648 printf ("tab_joint_text(): bad cell "
649 "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
650 x1, table->col_ofs, x1 + table->col_ofs,
651 y1, table->row_ofs, y1 + table->row_ofs,
652 x2, table->col_ofs, x2 + table->col_ofs,
653 y2, table->row_ofs, y2 + table->row_ofs,
654 table->nc, table->nr);
659 tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
661 j = pool_alloc (table->container, sizeof *j);
663 j->x1 = x1 + table->col_ofs;
664 j->y1 = y1 + table->row_ofs;
665 j->x2 = ++x2 + table->col_ofs;
666 j->y2 = ++y2 + table->row_ofs;
671 va_start (args, text);
672 j->contents = text_format (table, opt, text, args);
679 struct substring *cc = &table->cc[x1 + y1 * table->cf];
680 unsigned char *ct = &table->ct[x1 + y1 * table->cf];
681 const int ofs = table->cf - (x2 - x1);
685 for (y = y1; y < y2; y++)
689 for (x = x1; x < x2; x++)
691 *cc++ = ss_buffer ((char *) j, 0);
701 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
703 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
704 struct substring *string)
706 assert (table != NULL && string != NULL);
709 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
710 || c + table->col_ofs >= table->nc
711 || r + table->row_ofs >= table->nr)
713 printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
715 c, table->col_ofs, c + table->col_ofs,
716 r, table->row_ofs, r + table->row_ofs,
717 table->nc, table->nr);
722 table->cc[c + r * table->cf] = *string;
723 table->ct[c + r * table->cf] = opt;
728 /* Sets the widths of all the columns and heights of all the rows in
729 table T for driver D. */
731 nowrap_dim (struct tab_table *t, struct outp_driver *d)
733 t->w[0] = tab_natural_width (t, d, 0);
734 t->h[0] = d->font_height;
737 /* Sets the widths of all the columns and heights of all the rows in
738 table T for driver D. */
740 wrap_dim (struct tab_table *t, struct outp_driver *d)
742 t->w[0] = tab_natural_width (t, d, 0);
743 t->h[0] = tab_natural_height (t, d, 0);
746 /* Outputs text BUF as a table with a single cell having cell options
747 OPTIONS, which is a combination of the TAB_* and TAT_*
750 tab_output_text (int options, const char *buf, ...)
752 struct tab_table *t = tab_create (1, 1, 0);
753 char *tmp_buf = NULL;
755 if (options & TAT_PRINTF)
759 va_start (args, buf);
760 buf = tmp_buf = xvasprintf (buf, args);
764 tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
765 tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
766 tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim);
772 /* Set table flags to FLAGS. */
774 tab_flags (struct tab_table *t, unsigned flags)
780 /* Easy, type-safe way to submit a tab table to som. */
782 tab_submit (struct tab_table *t)
787 s.class = &tab_table_class;
796 /* Set table row and column offsets for all functions that affect
799 tab_offset (struct tab_table *t, int col, int row)
805 if (row < -1 || row >= t->nr)
807 printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
810 if (col < -1 || col >= t->nc)
812 printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
818 diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
820 diff += (col - t->col_ofs), t->col_ofs = col;
826 /* Increment the row offset by one. If the table is too small,
827 increase its size. */
829 tab_next_row (struct tab_table *t)
834 if (++t->row_ofs >= t->nr)
835 tab_realloc (t, -1, t->nr * 4 / 3);
838 static struct tab_table *t;
839 static struct outp_driver *d;
842 /* Set the current table to TABLE. */
844 tabi_table (struct som_entity *table)
846 assert (table != NULL);
847 assert (table->type == SOM_TABLE);
850 tab_offset (t, 0, 0);
852 assert (t->w == NULL && t->h == NULL);
853 t->w = pool_nalloc (t->container, t->nc, sizeof *t->w);
854 t->h = pool_nalloc (t->container, t->nr, sizeof *t->h);
855 t->hrh = pool_nmalloc (t->container, t->nr + 1, sizeof *t->hrh);
856 t->wrv = pool_nmalloc (t->container, t->nc + 1, sizeof *t->wrv);
859 /* Returns the line style to use for spacing purposes for a rule
860 of the given TYPE. */
861 static enum outp_line_style
862 rule_to_spacing_type (unsigned char type)
870 return OUTP_L_SINGLE;
872 return OUTP_L_DOUBLE;
878 /* Set the current output device to DRIVER. */
880 tabi_driver (struct outp_driver *driver)
885 assert (driver != NULL);
888 /* Figure out sizes of rules. */
889 for (r = 0; r <= t->nr; r++)
892 for (c = 0; c < t->nc; c++)
894 unsigned char rh = t->rh[c + r * t->cf];
895 int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
902 for (c = 0; c <= t->nc; c++)
905 for (r = 0; r < t->nr; r++)
907 unsigned char *rv = &t->rv[c + r * (t->cf + 1)];
909 if (*rv == UCHAR_MAX)
910 *rv = c != 0 && c != t->nc ? TAL_GAP : TAL_0;
911 w = driver->vert_line_width[rule_to_spacing_type (*rv)];
919 for (i = 0; i < t->nr; i++)
921 for (i = 0; i < t->nc; i++)
925 assert (t->dim != NULL);
932 for (i = 0; i < t->nr; i++)
936 printf ("Table row %d height not initialized.\n", i);
939 assert (t->h[i] > 0);
942 for (i = 0; i < t->nc; i++)
946 printf ("Table column %d width not initialized.\n", i);
949 assert (t->w[i] > 0);
954 /* Add up header sizes. */
955 for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
956 t->wl += t->w[i] + t->wrv[i + 1];
957 for (i = 0, t->ht = t->hrh[0]; i < t->t; i++)
958 t->ht += t->h[i] + t->hrh[i + 1];
959 for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++)
960 t->wr += t->w[i] + t->wrv[i + 1];
961 for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
962 t->hb += t->h[i] + t->hrh[i + 1];
965 if (!(t->flags & SOMF_NO_TITLE))
966 t->ht += d->font_height;
969 /* Return the number of columns and rows in the table into N_COLUMNS
970 and N_ROWS, respectively. */
972 tabi_count (int *n_columns, int *n_rows)
974 assert (n_columns != NULL && n_rows != NULL);
979 static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual);
981 /* Return the horizontal and vertical size of the entire table,
982 including headers, for the current output device, into HORIZ and
985 tabi_area (int *horiz, int *vert)
987 assert (horiz != NULL && vert != NULL);
992 for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
993 c < t->nc - t->r; c++)
994 w += t->w[c] + t->wrv[c];
1000 for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
1001 r < t->nr - t->b; r++)
1002 h += t->h[r] + t->hrh[r];
1007 /* Return the column style for this table into STYLE. */
1009 tabi_columns (int *style)
1011 assert (style != NULL);
1012 *style = t->col_style;
1015 /* Return the number of header rows/columns on the left, right, top,
1016 and bottom sides into HL, HR, HT, and HB, respectively. */
1018 tabi_headers (int *hl, int *hr, int *ht, int *hb)
1020 assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL);
1027 /* Determines the number of rows or columns (including appropriate
1028 headers), depending on CUMTYPE, that will fit into the space
1029 specified. Takes rows/columns starting at index START and attempts
1030 to fill up available space MAX. Returns in END the index of the
1031 last row/column plus one; returns in ACTUAL the actual amount of
1032 space the selected rows/columns (including appropriate headers)
1035 tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
1042 assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1043 if (cumtype == SOM_ROWS)
1045 assert (start >= 0 && start < t->nr);
1048 r = &t->hrh[start + 1];
1049 total = t->ht + t->hb;
1053 assert (start >= 0 && start < t->nc);
1056 r = &t->wrv[start + 1];
1057 total = t->wl + t->wr;
1073 for (x = start + 1; x < n; x++)
1075 int amt = *d++ + *r++;
1093 /* Return flags set for the current table into FLAGS. */
1095 tabi_flags (unsigned *flags)
1097 assert (flags != NULL);
1101 /* Returns true if the table will fit in the given page WIDTH,
1104 tabi_fits_width (int width)
1108 for (i = t->l; i < t->nc - t->r; i++)
1109 if (t->wl + t->wr + t->w[i] > width)
1115 /* Returns true if the table will fit in the given page LENGTH,
1118 tabi_fits_length (int length)
1122 for (i = t->t; i < t->nr - t->b; i++)
1123 if (t->ht + t->hb + t->h[i] > length)
1129 /* Sets the number of header rows/columns on the left, right, top,
1130 and bottom sides to HL, HR, HT, and HB, respectively. */
1132 tabi_set_headers (int hl, int hr, int ht, int hb)
1140 /* Render title for current table, with major index X and minor index
1141 Y. Y may be zero, or X and Y may be zero, but X should be nonzero
1144 tabi_title (int x, int y)
1149 if (t->flags & SOMF_NO_TITLE)
1152 cp = spprintf (buf, "%d.%d", table_num, subtable_num);
1154 cp = spprintf (cp, "(%d:%d)", x, y);
1156 cp = spprintf (cp, "(%d)", x);
1157 if (command_name != NULL)
1158 cp = spprintf (cp, " %s", command_name);
1159 cp = stpcpy (cp, ". ");
1160 if (t->title != NULL)
1162 size_t length = strlen (t->title);
1163 memcpy (cp, t->title, length);
1169 struct outp_text text;
1171 text.font = OUTP_PROPORTIONAL;
1172 text.justification = OUTP_LEFT;
1173 text.string = ss_buffer (buf, cp - buf);
1175 text.v = d->font_height;
1178 d->class->text_draw (d, &text);
1182 static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
1184 /* Renders columns C0...C1, plus headers, of rows R0...R1,
1185 at the given vertical position Y.
1186 C0 and C1 count vertical rules as columns,
1187 but R0 and R1 do not count horizontal rules as rows.
1188 Returns the vertical position after rendering. */
1190 render_rows (int y, int c0, int c1, int r0, int r1)
1193 for (r = r0; r < r1; r++)
1196 x = render_strip (x, y, r, 0, t->l * 2 + 1, r0, r1);
1197 x = render_strip (x, y, r, c0 * 2 + 1, c1 * 2, r0, r1);
1198 x = render_strip (x, y, r, (t->nc - t->r) * 2, t->nc * 2 + 1, r0, r1);
1199 y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2];
1204 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1205 current position on the current output device. */
1207 tabi_render (int c0, int r0, int c1, int r1)
1214 if (!(t->flags & SOMF_NO_TITLE))
1215 y += d->font_height;
1217 y = render_rows (y, c0, c1, 0, t->t * 2 + 1);
1218 y = render_rows (y, c0, c1, r0 * 2 + 1, r1 * 2);
1219 y = render_rows (y, c0, c1, (t->nr - t->b) * 2, t->nr * 2 + 1);
1222 struct som_table_class tab_table_class =
1248 static enum outp_justification
1249 translate_justification (unsigned int opt)
1251 switch (opt & TAB_ALIGN_MASK)
1264 /* Returns the line style to use for drawing a rule of the given
1266 static enum outp_line_style
1267 rule_to_draw_type (unsigned char type)
1275 return OUTP_L_SINGLE;
1277 return OUTP_L_DOUBLE;
1283 /* Returns the horizontal rule at the given column and row. */
1285 get_hrule (int c, int r)
1287 return t->rh[c + r * t->cf];
1290 /* Returns the vertical rule at the given column and row. */
1292 get_vrule (int c, int r)
1294 return t->rv[c + r * (t->cf + 1)];
1297 /* Renders the horizontal rule at the given column and row
1298 at (X,Y) on the page. */
1300 render_horz_rule (int x, int y, int c, int r)
1302 enum outp_line_style style = rule_to_draw_type (get_hrule (c, r));
1303 if (style != OUTP_L_NONE)
1304 d->class->line (d, x, y, x + t->w[c], y + t->hrh[r],
1305 OUTP_L_NONE, style, OUTP_L_NONE, style);
1308 /* Renders the vertical rule at the given column and row
1309 at (X,Y) on the page. */
1311 render_vert_rule (int x, int y, int c, int r)
1313 enum outp_line_style style = rule_to_draw_type (get_vrule (c, r));
1314 if (style != OUTP_L_NONE)
1315 d->class->line (d, x, y, x + t->wrv[c], y + t->h[r],
1316 style, OUTP_L_NONE, style, OUTP_L_NONE);
1319 /* Renders the rule intersection at the given column and row
1320 at (X,Y) on the page. */
1322 render_rule_intersection (int x, int y, int c, int r)
1324 /* Bounds of intersection. */
1327 int x1 = x + t->wrv[c];
1328 int y1 = y + t->hrh[r];
1330 /* Lines on each side of intersection. */
1331 int top = r > 0 ? get_vrule (c, r - 1) : TAL_0;
1332 int left = c > 0 ? get_hrule (c - 1, r) : TAL_0;
1333 int bottom = r < t->nr ? get_vrule (c, r) : TAL_0;
1334 int right = c < t->nc ? get_hrule (c, r) : TAL_0;
1336 /* Output style for each line. */
1337 enum outp_line_style o_top = rule_to_draw_type (top);
1338 enum outp_line_style o_left = rule_to_draw_type (left);
1339 enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1340 enum outp_line_style o_right = rule_to_draw_type (right);
1342 if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1343 || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1344 d->class->line (d, x0, y0, x1, y1, o_top, o_left, o_bottom, o_right);
1347 /* Returns the width of columns C1...C2 exclusive,
1348 including interior but not exterior rules. */
1350 strip_width (int c1, int c2)
1355 for (c = c1; c < c2; c++)
1356 width += t->w[c] + t->wrv[c + 1];
1358 width -= t->wrv[c2];
1362 /* Returns the height of rows R1...R2 exclusive,
1363 including interior but not exterior rules. */
1365 strip_height (int r1, int r2)
1370 for (r = r1; r < r2; r++)
1371 height += t->h[r] + t->hrh[r + 1];
1373 height -= t->hrh[r2];
1377 /* Renders the cell at the given column and row at (X,Y) on the
1378 page. Also renders joined cells that extend as far to the
1379 right as C1 and as far down as R1. */
1381 render_cell (int x, int y, int c, int r, int c1, int r1)
1383 const int index = c + (r * t->cf);
1384 unsigned char type = t->ct[index];
1385 struct substring *content = &t->cc[index];
1387 if (!(type & TAB_JOIN))
1389 if (!(type & TAB_EMPTY))
1391 struct outp_text text;
1392 text.font = options_to_font (type);
1393 text.justification = translate_justification (type);
1394 text.string = *content;
1399 d->class->text_draw (d, &text);
1404 struct tab_joined_cell *j
1405 = (struct tab_joined_cell *) ss_data (*content);
1407 if (j->hit != tab_hit)
1411 if (j->x1 == c && j->y1 == r)
1413 struct outp_text text;
1414 text.font = options_to_font (type);
1415 text.justification = translate_justification (type);
1416 text.string = j->contents;
1419 text.h = strip_width (j->x1, MIN (j->x2, c1));
1420 text.v = strip_height (j->y1, MIN (j->y2, r1));
1421 d->class->text_draw (d, &text);
1427 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1428 on row R, at (X,Y). Returns X position after rendering.
1429 Also renders joined cells that extend beyond that strip,
1430 cropping them to lie within rendering region (C0,R0)-(C1,R1).
1431 C0 and C1 count vertical rules as columns.
1432 R counts horizontal rules as rows, but R0 and R1 do not. */
1434 render_strip (int x, int y, int r, int c0, int c1, int r0 UNUSED, int r1)
1438 for (c = c0; c < c1; c++)
1442 render_cell (x, y, c / 2, r / 2, c1 / 2, r1);
1444 render_horz_rule (x, y, c / 2, r / 2);
1450 render_vert_rule (x, y, c / 2, r / 2);
1452 render_rule_intersection (x, y, c / 2, r / 2);
1459 /* Sets COMMAND_NAME as the name of the current command,
1460 for embedding in output. */
1462 tab_set_command_name (const char *command_name_)
1464 free (command_name);
1465 command_name = command_name_ ? xstrdup (command_name_) : NULL;