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->hrh = pool_nmalloc (t->container, nr + 1, sizeof *t->hrh);
75 memset (t->hrh, 0, sizeof *t->hrh * (nr + 1));
77 t->rv = pool_nmalloc (t->container, nr, nc + 1);
78 memset (t->rv, UCHAR_MAX, nr * (nc + 1));
80 t->wrv = pool_nmalloc (t->container, nc + 1, sizeof *t->wrv);
81 memset (t->wrv, 0, sizeof *t->wrv * (nc + 1));
85 t->col_ofs = t->row_ofs = 0;
90 /* Destroys table T. */
92 tab_destroy (struct tab_table *t)
95 pool_destroy (t->container);
98 /* Sets the width and height of a table, in columns and rows,
99 respectively. Use only to reduce the size of a table, since it
100 does not change the amount of allocated memory. */
102 tab_resize (struct tab_table *t, int nc, int nr)
107 assert (nc + t->col_ofs <= t->cf);
108 t->nc = nc + t->col_ofs;
112 assert (nr + t->row_ofs <= t->nr);
113 t->nr = nr + t->row_ofs;
117 /* Changes either or both dimensions of a table. Consider using the
118 above routine instead if it won't waste a lot of space.
120 Changing the number of columns in a table is particularly expensive
121 in space and time. Avoid doing such. FIXME: In fact, transferring
122 of rules isn't even implemented yet. */
124 tab_realloc (struct tab_table *t, int nc, int nr)
132 tab_offset (t, 0, 0);
139 assert (nc == t->nc);
143 int mr1 = min (nr, t->nr);
144 int mc1 = min (nc, t->nc);
146 struct substring *new_cc;
147 unsigned char *new_ct;
150 new_cc = pool_nmalloc (t->container, nr * nc, sizeof *new_cc);
151 new_ct = pool_malloc (t->container, nr * nc);
152 for (r = 0; r < mr1; r++)
154 memcpy (&new_cc[r * nc], &t->cc[r * t->nc], mc1 * sizeof *t->cc);
155 memcpy (&new_ct[r * nc], &t->ct[r * t->nc], mc1);
156 memset (&new_ct[r * nc + t->nc], TAB_EMPTY, nc - t->nc);
158 pool_free (t->container, t->cc);
159 pool_free (t->container, t->ct);
164 else if (nr != t->nr)
166 t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc);
167 t->ct = pool_realloc (t->container, t->ct, nr * nc);
169 t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
170 t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
174 memset (&t->rh[nc * (t->nr + 1)], TAL_0, (nr - t->nr) * nc);
175 memset (&t->rv[(nc + 1) * t->nr], UCHAR_MAX,
176 (nr - t->nr) * (nc + 1));
180 memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr));
186 tab_offset (t, co, ro);
189 /* Sets the number of header rows on each side of TABLE to L on the
190 left, R on the right, T on the top, B on the bottom. Header rows
191 are repeated when a table is broken across multiple columns or
194 tab_headers (struct tab_table *table, int l, int r, int t, int b)
196 assert (table != NULL);
197 assert (l < table->nc);
198 assert (r < table->nc);
199 assert (t < table->nr);
200 assert (b < table->nr);
209 /* Set up table T so that, when it is an appropriate size, it will be
210 displayed across the page in columns.
212 STYLE is a TAB_COL_* constant. GROUP is the number of rows to take
215 tab_columns (struct tab_table *t, int style, int group)
218 t->col_style = style;
219 t->col_group = group;
224 /* Draws a vertical line to the left of cells at horizontal position X
225 from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
227 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
232 if (x + t->col_ofs < 0 || x + t->col_ofs > t->nc
233 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
234 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
236 printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
237 "table size (%d,%d)\n"),
238 x, t->col_ofs, x + t->col_ofs,
239 y1, t->row_ofs, y1 + t->row_ofs,
240 y2, t->row_ofs, y2 + t->row_ofs,
254 assert (y2 <= t->nr);
259 for (y = y1; y <= y2; y++)
260 t->rv[x + (t->cf + 1) * y] = style;
264 /* Draws a horizontal line above cells at vertical position Y from X1
265 to X2 inclusive in style STYLE, if style is not -1. */
267 tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
284 for (x = x1; x <= x2; x++)
285 t->rh[x + t->cf * y] = style;
289 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
290 lines of style F_H and vertical lines of style F_V. Fills the
291 interior of the box with horizontal lines of style I_H and vertical
292 lines of style I_V. Any of the line styles may be -1 to avoid
293 drawing those lines. This is distinct from 0, which draws a null
296 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
297 int x1, int y1, int x2, int y2)
302 if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc
303 || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= t->nc
304 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
305 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
307 printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
308 "in table size (%d,%d)\n"),
309 x1, t->col_ofs, x1 + t->col_ofs,
310 y1, t->row_ofs, y1 + t->row_ofs,
311 x2, t->col_ofs, x2 + t->col_ofs,
312 y2, t->row_ofs, y2 + t->row_ofs,
333 for (x = x1; x <= x2; x++)
335 t->rh[x + t->cf * y1] = f_h;
336 t->rh[x + t->cf * (y2 + 1)] = f_h;
342 for (y = y1; y <= y2; y++)
344 t->rv[x1 + (t->cf + 1) * y] = f_v;
345 t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
353 for (y = y1 + 1; y <= y2; y++)
357 for (x = x1; x <= x2; x++)
358 t->rh[x + t->cf * y] = i_h;
365 for (x = x1 + 1; x <= x2; x++)
369 for (y = y1; y <= y2; y++)
370 t->rv[x + (t->cf + 1) * y] = i_v;
375 /* Formats text TEXT and arguments ARGS as indicated in OPT in
376 TABLE's pool and returns the resultant string. */
377 static struct substring
378 text_format (struct tab_table *table, int opt, const char *text, va_list args)
380 assert (table != NULL && text != NULL);
382 return ss_cstr (opt & TAT_PRINTF
383 ? pool_vasprintf (table->container, text, args)
384 : pool_strdup (table->container, text));
387 /* Set the title of table T to TITLE, which is formatted as if
388 passed to printf(). */
390 tab_title (struct tab_table *t, const char *title, ...)
394 assert (t != NULL && title != NULL);
395 va_start (args, title);
396 t->title = xvasprintf (title, args);
400 /* Set DIM_FUNC as the dimension function for table T. */
402 tab_dim (struct tab_table *t, tab_dim_func *dim_func)
404 assert (t != NULL && t->dim == NULL);
408 /* Returns the natural width of column C in table T for driver D, that
409 is, the smallest width necessary to display all its cells without
410 wrapping. The width will be no larger than the page width minus
411 left and right rule widths. */
413 tab_natural_width (struct tab_table *t, struct outp_driver *d, int c)
417 assert (t != NULL && c >= 0 && c < t->nc);
421 for (width = r = 0; r < t->nr; r++)
423 struct outp_text text;
424 unsigned char opt = t->ct[c + r * t->cf];
427 if (opt & (TAB_JOIN | TAB_EMPTY))
430 text.string = t->cc[c + r * t->cf];
431 text.justification = OUTP_LEFT;
432 text.font = options_to_font (opt);
433 text.h = text.v = INT_MAX;
435 d->class->text_metrics (d, &text, &w, NULL);
443 width = d->prop_em_width * 8;
445 printf ("warning: table column %d contains no data.\n", c);
450 const int clamp = d->width - t->wrv[0] - t->wrv[t->nc];
459 /* Returns the natural height of row R in table T for driver D, that
460 is, the minimum height necessary to display the information in the
461 cell at the widths set for each column. */
463 tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
467 assert (t != NULL && r >= 0 && r < t->nr);
472 for (height = d->font_height, c = 0; c < t->nc; c++)
474 struct outp_text text;
475 unsigned char opt = t->ct[c + r * t->cf];
478 assert (t->w[c] != NOT_INT);
479 if (opt & (TAB_JOIN | TAB_EMPTY))
482 text.string = t->cc[c + r * t->cf];
483 text.justification = OUTP_LEFT;
484 text.font = options_to_font (opt);
487 d->class->text_metrics (d, &text, NULL, &h);
497 /* Callback function to set all columns and rows to their natural
498 dimensions. Not really meant to be called directly. */
500 tab_natural_dimensions (struct tab_table *t, struct outp_driver *d)
506 for (i = 0; i < t->nc; i++)
507 t->w[i] = tab_natural_width (t, d, i);
509 for (i = 0; i < t->nr; i++)
510 t->h[i] = tab_natural_height (t, d, i);
516 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
517 from V, displayed with format spec F. */
519 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
520 const union value *v, const struct fmt_spec *f)
524 assert (table != NULL && v != NULL && f != NULL);
526 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
527 || c + table->col_ofs >= table->nc
528 || r + table->row_ofs >= table->nr)
530 printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
532 c, table->col_ofs, c + table->col_ofs,
533 r, table->row_ofs, r + table->row_ofs,
534 table->nc, table->nr);
539 contents = pool_alloc (table->container, f->w);
540 table->cc[c + r * table->cf] = ss_buffer (contents, f->w);
541 table->ct[c + r * table->cf] = opt;
543 data_out (contents, f, v);
546 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
547 with NDEC decimal places. */
549 tab_float (struct tab_table *table, int c, int r, unsigned char opt,
550 double val, int w, int d)
556 union value double_value;
558 assert (table != NULL && w <= 40);
561 assert (c < table->nc);
563 assert (r < table->nr);
565 f = make_output_format (FMT_F, w, d);
568 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
569 || c + table->col_ofs >= table->nc
570 || r + table->row_ofs >= table->nr)
572 printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
574 c, table->col_ofs, c + table->col_ofs,
575 r, table->row_ofs, r + table->row_ofs,
576 table->nc, table->nr);
581 double_value.f = val;
582 data_out (buf, &f, &double_value);
585 while (isspace ((unsigned char) *cp) && cp < &buf[w])
587 f.w = w - (cp - buf);
589 contents = pool_alloc (table->container, f.w);
590 table->cc[c + r * table->cf] = ss_buffer (contents, f.w);
591 table->ct[c + r * table->cf] = opt;
592 memcpy (contents, cp, f.w);
595 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
598 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
602 assert (table != NULL && text != NULL);
606 assert (c < table->nc);
607 assert (r < table->nr);
611 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
612 || c + table->col_ofs >= table->nc
613 || r + table->row_ofs >= table->nr)
615 printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
617 c, table->col_ofs, c + table->col_ofs,
618 r, table->row_ofs, r + table->row_ofs,
619 table->nc, table->nr);
624 va_start (args, text);
625 table->cc[c + r * table->cf] = text_format (table, opt, text, args);
626 table->ct[c + r * table->cf] = opt;
630 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
631 options OPT to have text value TEXT. */
633 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
634 unsigned opt, const char *text, ...)
636 struct tab_joined_cell *j;
638 assert (table != NULL && text != NULL);
640 assert (x1 + table->col_ofs >= 0);
641 assert (y1 + table->row_ofs >= 0);
644 assert (y2 + table->row_ofs < table->nr);
645 assert (x2 + table->col_ofs < table->nc);
648 if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
649 || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
650 || x2 < x1 || x2 + table->col_ofs >= table->nc
651 || y2 < y2 || y2 + table->row_ofs >= table->nr)
653 printf ("tab_joint_text(): bad cell "
654 "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
655 x1, table->col_ofs, x1 + table->col_ofs,
656 y1, table->row_ofs, y1 + table->row_ofs,
657 x2, table->col_ofs, x2 + table->col_ofs,
658 y2, table->row_ofs, y2 + table->row_ofs,
659 table->nc, table->nr);
664 tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
666 j = pool_alloc (table->container, sizeof *j);
668 j->x1 = x1 + table->col_ofs;
669 j->y1 = y1 + table->row_ofs;
670 j->x2 = ++x2 + table->col_ofs;
671 j->y2 = ++y2 + table->row_ofs;
676 va_start (args, text);
677 j->contents = text_format (table, opt, text, args);
684 struct substring *cc = &table->cc[x1 + y1 * table->cf];
685 unsigned char *ct = &table->ct[x1 + y1 * table->cf];
686 const int ofs = table->cf - (x2 - x1);
690 for (y = y1; y < y2; y++)
694 for (x = x1; x < x2; x++)
696 *cc++ = ss_buffer ((char *) j, 0);
706 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
708 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
709 struct substring *string)
711 assert (table != NULL && string != NULL);
714 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
715 || c + table->col_ofs >= table->nc
716 || r + table->row_ofs >= table->nr)
718 printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
720 c, table->col_ofs, c + table->col_ofs,
721 r, table->row_ofs, r + table->row_ofs,
722 table->nc, table->nr);
727 table->cc[c + r * table->cf] = *string;
728 table->ct[c + r * table->cf] = opt;
733 /* Sets the widths of all the columns and heights of all the rows in
734 table T for driver D. */
736 nowrap_dim (struct tab_table *t, struct outp_driver *d)
738 t->w[0] = tab_natural_width (t, d, 0);
739 t->h[0] = d->font_height;
742 /* Sets the widths of all the columns and heights of all the rows in
743 table T for driver D. */
745 wrap_dim (struct tab_table *t, struct outp_driver *d)
747 t->w[0] = tab_natural_width (t, d, 0);
748 t->h[0] = tab_natural_height (t, d, 0);
751 /* Outputs text BUF as a table with a single cell having cell options
752 OPTIONS, which is a combination of the TAB_* and TAT_*
755 tab_output_text (int options, const char *buf, ...)
757 struct tab_table *t = tab_create (1, 1, 0);
758 char *tmp_buf = NULL;
760 if (options & TAT_PRINTF)
764 va_start (args, buf);
765 buf = tmp_buf = xvasprintf (buf, args);
769 tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
770 tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
771 tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim);
777 /* Set table flags to FLAGS. */
779 tab_flags (struct tab_table *t, unsigned flags)
785 /* Easy, type-safe way to submit a tab table to som. */
787 tab_submit (struct tab_table *t)
792 s.class = &tab_table_class;
801 /* Set table row and column offsets for all functions that affect
804 tab_offset (struct tab_table *t, int col, int row)
810 if (row < -1 || row >= t->nr)
812 printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
815 if (col < -1 || col >= t->nc)
817 printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
823 diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
825 diff += (col - t->col_ofs), t->col_ofs = col;
831 /* Increment the row offset by one. If the table is too small,
832 increase its size. */
834 tab_next_row (struct tab_table *t)
839 if (++t->row_ofs >= t->nr)
840 tab_realloc (t, -1, t->nr * 4 / 3);
843 static struct tab_table *t;
844 static struct outp_driver *d;
847 /* Set the current table to TABLE. */
849 tabi_table (struct som_entity *table)
851 assert (table != NULL);
852 assert (table->type == SOM_TABLE);
855 tab_offset (t, 0, 0);
857 assert (t->w == NULL && t->h == NULL);
858 t->w = pool_nalloc (t->container, t->nc, sizeof *t->w);
859 t->h = pool_nalloc (t->container, t->nr, sizeof *t->h);
862 /* Returns the line style to use for spacing purposes for a rule
863 of the given TYPE. */
864 static enum outp_line_style
865 rule_to_spacing_type (unsigned char type)
873 return OUTP_L_SINGLE;
875 return OUTP_L_DOUBLE;
881 /* Set the current output device to DRIVER. */
883 tabi_driver (struct outp_driver *driver)
888 assert (driver != NULL);
891 /* Figure out sizes of rules. */
892 for (r = 0; r <= t->nr; r++)
895 for (c = 0; c < t->nc; c++)
897 unsigned char rh = t->rh[c + r * t->cf];
898 int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
905 for (c = 0; c <= t->nc; c++)
908 for (r = 0; r < t->nr; r++)
910 unsigned char *rv = &t->rv[c + r * (t->cf + 1)];
912 if (*rv == UCHAR_MAX)
913 *rv = c != 0 && c != t->nc ? TAL_GAP : TAL_0;
914 w = driver->vert_line_width[rule_to_spacing_type (*rv)];
922 for (i = 0; i < t->nr; i++)
924 for (i = 0; i < t->nc; i++)
928 assert (t->dim != NULL);
935 for (i = 0; i < t->nr; i++)
939 printf ("Table row %d height not initialized.\n", i);
942 assert (t->h[i] > 0);
945 for (i = 0; i < t->nc; i++)
949 printf ("Table column %d width not initialized.\n", i);
952 assert (t->w[i] > 0);
957 /* Add up header sizes. */
958 for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
959 t->wl += t->w[i] + t->wrv[i + 1];
960 for (i = 0, t->ht = t->hrh[0]; i < t->t; i++)
961 t->ht += t->h[i] + t->hrh[i + 1];
962 for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++)
963 t->wr += t->w[i] + t->wrv[i + 1];
964 for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
965 t->hb += t->h[i] + t->hrh[i + 1];
968 if (!(t->flags & SOMF_NO_TITLE))
969 t->ht += d->font_height;
972 /* Return the number of columns and rows in the table into N_COLUMNS
973 and N_ROWS, respectively. */
975 tabi_count (int *n_columns, int *n_rows)
977 assert (n_columns != NULL && n_rows != NULL);
982 static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual);
984 /* Return the horizontal and vertical size of the entire table,
985 including headers, for the current output device, into HORIZ and
988 tabi_area (int *horiz, int *vert)
990 assert (horiz != NULL && vert != NULL);
995 for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
996 c < t->nc - t->r; c++)
997 w += t->w[c] + t->wrv[c];
1003 for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
1004 r < t->nr - t->b; r++)
1005 h += t->h[r] + t->hrh[r];
1010 /* Return the column style for this table into STYLE. */
1012 tabi_columns (int *style)
1014 assert (style != NULL);
1015 *style = t->col_style;
1018 /* Return the number of header rows/columns on the left, right, top,
1019 and bottom sides into HL, HR, HT, and HB, respectively. */
1021 tabi_headers (int *hl, int *hr, int *ht, int *hb)
1023 assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL);
1030 /* Determines the number of rows or columns (including appropriate
1031 headers), depending on CUMTYPE, that will fit into the space
1032 specified. Takes rows/columns starting at index START and attempts
1033 to fill up available space MAX. Returns in END the index of the
1034 last row/column plus one; returns in ACTUAL the actual amount of
1035 space the selected rows/columns (including appropriate headers)
1038 tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
1045 assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1046 if (cumtype == SOM_ROWS)
1048 assert (start >= 0 && start < t->nr);
1051 r = &t->hrh[start + 1];
1052 total = t->ht + t->hb;
1056 assert (start >= 0 && start < t->nc);
1059 r = &t->wrv[start + 1];
1060 total = t->wl + t->wr;
1076 for (x = start + 1; x < n; x++)
1078 int amt = *d++ + *r++;
1096 /* Return flags set for the current table into FLAGS. */
1098 tabi_flags (unsigned *flags)
1100 assert (flags != NULL);
1104 /* Returns true if the table will fit in the given page WIDTH,
1107 tabi_fits_width (int width)
1111 for (i = t->l; i < t->nc - t->r; i++)
1112 if (t->wl + t->wr + t->w[i] > width)
1118 /* Returns true if the table will fit in the given page LENGTH,
1121 tabi_fits_length (int length)
1125 for (i = t->t; i < t->nr - t->b; i++)
1126 if (t->ht + t->hb + t->h[i] > length)
1132 /* Sets the number of header rows/columns on the left, right, top,
1133 and bottom sides to HL, HR, HT, and HB, respectively. */
1135 tabi_set_headers (int hl, int hr, int ht, int hb)
1143 /* Render title for current table, with major index X and minor index
1144 Y. Y may be zero, or X and Y may be zero, but X should be nonzero
1147 tabi_title (int x, int y)
1152 if (t->flags & SOMF_NO_TITLE)
1155 cp = spprintf (buf, "%d.%d", table_num, subtable_num);
1157 cp = spprintf (cp, "(%d:%d)", x, y);
1159 cp = spprintf (cp, "(%d)", x);
1160 if (command_name != NULL)
1161 cp = spprintf (cp, " %s", command_name);
1162 cp = stpcpy (cp, ". ");
1163 if (t->title != NULL)
1165 size_t length = strlen (t->title);
1166 memcpy (cp, t->title, length);
1172 struct outp_text text;
1174 text.font = OUTP_PROPORTIONAL;
1175 text.justification = OUTP_LEFT;
1176 text.string = ss_buffer (buf, cp - buf);
1178 text.v = d->font_height;
1181 d->class->text_draw (d, &text);
1185 static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
1187 /* Renders columns C0...C1, plus headers, of rows R0...R1,
1188 at the given vertical position Y.
1189 C0 and C1 count vertical rules as columns,
1190 but R0 and R1 do not count horizontal rules as rows.
1191 Returns the vertical position after rendering. */
1193 render_rows (int y, int c0, int c1, int r0, int r1)
1196 for (r = r0; r < r1; r++)
1199 x = render_strip (x, y, r, 0, t->l * 2 + 1, r0, r1);
1200 x = render_strip (x, y, r, c0 * 2 + 1, c1 * 2, r0, r1);
1201 x = render_strip (x, y, r, (t->nc - t->r) * 2, t->nc * 2 + 1, r0, r1);
1202 y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2];
1207 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1208 current position on the current output device. */
1210 tabi_render (int c0, int r0, int c1, int r1)
1217 if (!(t->flags & SOMF_NO_TITLE))
1218 y += d->font_height;
1220 y = render_rows (y, c0, c1, 0, t->t * 2 + 1);
1221 y = render_rows (y, c0, c1, r0 * 2 + 1, r1 * 2);
1222 y = render_rows (y, c0, c1, (t->nr - t->b) * 2, t->nr * 2 + 1);
1225 struct som_table_class tab_table_class =
1251 static enum outp_justification
1252 translate_justification (unsigned int opt)
1254 switch (opt & TAB_ALIGN_MASK)
1267 /* Returns the line style to use for drawing a rule of the given
1269 static enum outp_line_style
1270 rule_to_draw_type (unsigned char type)
1278 return OUTP_L_SINGLE;
1280 return OUTP_L_DOUBLE;
1286 /* Returns the horizontal rule at the given column and row. */
1288 get_hrule (int c, int r)
1290 return t->rh[c + r * t->cf];
1293 /* Returns the vertical rule at the given column and row. */
1295 get_vrule (int c, int r)
1297 return t->rv[c + r * (t->cf + 1)];
1300 /* Renders the horizontal rule at the given column and row
1301 at (X,Y) on the page. */
1303 render_horz_rule (int x, int y, int c, int r)
1305 enum outp_line_style style = rule_to_draw_type (get_hrule (c, r));
1306 if (style != OUTP_L_NONE)
1307 d->class->line (d, x, y, x + t->w[c], y + t->hrh[r],
1308 OUTP_L_NONE, style, OUTP_L_NONE, style);
1311 /* Renders the vertical rule at the given column and row
1312 at (X,Y) on the page. */
1314 render_vert_rule (int x, int y, int c, int r)
1316 enum outp_line_style style = rule_to_draw_type (get_vrule (c, r));
1317 if (style != OUTP_L_NONE)
1318 d->class->line (d, x, y, x + t->wrv[c], y + t->h[r],
1319 style, OUTP_L_NONE, style, OUTP_L_NONE);
1322 /* Renders the rule intersection at the given column and row
1323 at (X,Y) on the page. */
1325 render_rule_intersection (int x, int y, int c, int r)
1327 /* Bounds of intersection. */
1330 int x1 = x + t->wrv[c];
1331 int y1 = y + t->hrh[r];
1333 /* Lines on each side of intersection. */
1334 int top = r > 0 ? get_vrule (c, r - 1) : TAL_0;
1335 int left = c > 0 ? get_hrule (c - 1, r) : TAL_0;
1336 int bottom = r < t->nr ? get_vrule (c, r) : TAL_0;
1337 int right = c < t->nc ? get_hrule (c, r) : TAL_0;
1339 /* Output style for each line. */
1340 enum outp_line_style o_top = rule_to_draw_type (top);
1341 enum outp_line_style o_left = rule_to_draw_type (left);
1342 enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1343 enum outp_line_style o_right = rule_to_draw_type (right);
1345 if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1346 || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1347 d->class->line (d, x0, y0, x1, y1, o_top, o_left, o_bottom, o_right);
1350 /* Returns the width of columns C1...C2 exclusive,
1351 including interior but not exterior rules. */
1353 strip_width (int c1, int c2)
1358 for (c = c1; c < c2; c++)
1359 width += t->w[c] + t->wrv[c + 1];
1361 width -= t->wrv[c2];
1365 /* Returns the height of rows R1...R2 exclusive,
1366 including interior but not exterior rules. */
1368 strip_height (int r1, int r2)
1373 for (r = r1; r < r2; r++)
1374 height += t->h[r] + t->hrh[r + 1];
1376 height -= t->hrh[r2];
1380 /* Renders the cell at the given column and row at (X,Y) on the
1381 page. Also renders joined cells that extend as far to the
1382 right as C1 and as far down as R1. */
1384 render_cell (int x, int y, int c, int r, int c1, int r1)
1386 const int index = c + (r * t->cf);
1387 unsigned char type = t->ct[index];
1388 struct substring *content = &t->cc[index];
1390 if (!(type & TAB_JOIN))
1392 if (!(type & TAB_EMPTY))
1394 struct outp_text text;
1395 text.font = options_to_font (type);
1396 text.justification = translate_justification (type);
1397 text.string = *content;
1402 d->class->text_draw (d, &text);
1407 struct tab_joined_cell *j
1408 = (struct tab_joined_cell *) ss_data (*content);
1410 if (j->hit != tab_hit)
1414 if (j->x1 == c && j->y1 == r)
1416 struct outp_text text;
1417 text.font = options_to_font (type);
1418 text.justification = translate_justification (type);
1419 text.string = j->contents;
1422 text.h = strip_width (j->x1, MIN (j->x2, c1));
1423 text.v = strip_height (j->y1, MIN (j->y2, r1));
1424 d->class->text_draw (d, &text);
1430 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1431 on row R, at (X,Y). Returns X position after rendering.
1432 Also renders joined cells that extend beyond that strip,
1433 cropping them to lie within rendering region (C0,R0)-(C1,R1).
1434 C0 and C1 count vertical rules as columns.
1435 R counts horizontal rules as rows, but R0 and R1 do not. */
1437 render_strip (int x, int y, int r, int c0, int c1, int r0 UNUSED, int r1)
1441 for (c = c0; c < c1; c++)
1445 render_cell (x, y, c / 2, r / 2, c1 / 2, r1);
1447 render_horz_rule (x, y, c / 2, r / 2);
1453 render_vert_rule (x, y, c / 2, r / 2);
1455 render_rule_intersection (x, y, c / 2, r / 2);
1462 /* Sets COMMAND_NAME as the name of the current command,
1463 for embedding in output. */
1465 tab_set_command_name (const char *command_name_)
1467 free (command_name);
1468 command_name = command_name_ ? xstrdup (command_name_) : NULL;