1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000, 2006 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
32 #include <data/data-out.h>
33 #include <data/format.h>
34 #include <data/variable.h>
35 #include <libpspp/alloc.h>
36 #include <libpspp/assertion.h>
37 #include <libpspp/compiler.h>
38 #include <libpspp/magic.h>
39 #include <libpspp/misc.h>
40 #include <libpspp/pool.h>
45 #define _(msgid) gettext (msgid)
47 const struct som_table_class tab_table_class;
48 static char *command_name;
50 /* Returns the font to use for a cell with the given OPTIONS. */
52 options_to_font (unsigned options)
54 return (options & TAB_FIX ? OUTP_FIXED
55 : options & TAB_EMPH ? OUTP_EMPHASIS
59 /* Creates a table with NC columns and NR rows. */
61 tab_create (int nc, int nr, int reallocable UNUSED)
65 t = pool_create_container (struct tab_table, container);
66 t->col_style = TAB_COL_NONE;
72 t->l = t->r = t->t = t->b = 0;
74 t->cc = pool_nmalloc (t->container, nr * nc, sizeof *t->cc);
75 t->ct = pool_malloc (t->container, nr * nc);
76 memset (t->ct, TAB_EMPTY, nc * nr);
78 t->rh = pool_nmalloc (t->container, nc, nr + 1);
79 memset (t->rh, 0, nc * (nr + 1));
81 t->rv = pool_nmalloc (t->container, nr, nc + 1);
82 memset (t->rv, UCHAR_MAX, nr * (nc + 1));
86 t->col_ofs = t->row_ofs = 0;
91 /* Destroys table T. */
93 tab_destroy (struct tab_table *t)
97 pool_destroy (t->container);
100 /* Sets the width and height of a table, in columns and rows,
101 respectively. Use only to reduce the size of a table, since it
102 does not change the amount of allocated memory. */
104 tab_resize (struct tab_table *t, int nc, int nr)
109 assert (nc + t->col_ofs <= t->cf);
110 t->nc = nc + t->col_ofs;
114 assert (nr + t->row_ofs <= t->nr);
115 t->nr = nr + t->row_ofs;
119 /* Changes either or both dimensions of a table. Consider using the
120 above routine instead if it won't waste a lot of space.
122 Changing the number of columns in a table is particularly expensive
123 in space and time. Avoid doing such. FIXME: In fact, transferring
124 of rules isn't even implemented yet. */
126 tab_realloc (struct tab_table *t, int nc, int nr)
134 tab_offset (t, 0, 0);
141 assert (nc == t->nc);
145 int mr1 = MIN (nr, t->nr);
146 int mc1 = MIN (nc, t->nc);
148 struct substring *new_cc;
149 unsigned char *new_ct;
152 new_cc = pool_nmalloc (t->container, nr * nc, sizeof *new_cc);
153 new_ct = pool_malloc (t->container, nr * nc);
154 for (r = 0; r < mr1; r++)
156 memcpy (&new_cc[r * nc], &t->cc[r * t->nc], mc1 * sizeof *t->cc);
157 memcpy (&new_ct[r * nc], &t->ct[r * t->nc], mc1);
158 memset (&new_ct[r * nc + t->nc], TAB_EMPTY, nc - t->nc);
160 pool_free (t->container, t->cc);
161 pool_free (t->container, t->ct);
166 else if (nr != t->nr)
168 t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc);
169 t->ct = pool_realloc (t->container, t->ct, nr * nc);
171 t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
172 t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
176 memset (&t->rh[nc * (t->nr + 1)], TAL_0, (nr - t->nr) * nc);
177 memset (&t->rv[(nc + 1) * t->nr], UCHAR_MAX,
178 (nr - t->nr) * (nc + 1));
182 memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr));
188 tab_offset (t, co, ro);
191 /* Sets the number of header rows on each side of TABLE to L on the
192 left, R on the right, T on the top, B on the bottom. Header rows
193 are repeated when a table is broken across multiple columns or
196 tab_headers (struct tab_table *table, int l, int r, int t, int b)
198 assert (table != NULL);
199 assert (l < table->nc);
200 assert (r < table->nc);
201 assert (t < table->nr);
202 assert (b < table->nr);
211 /* Set up table T so that, when it is an appropriate size, it will be
212 displayed across the page in columns.
214 STYLE is a TAB_COL_* constant. GROUP is the number of rows to take
217 tab_columns (struct tab_table *t, int style, int group)
220 t->col_style = style;
221 t->col_group = group;
226 /* Draws a vertical line to the left of cells at horizontal position X
227 from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
229 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
234 if (x + t->col_ofs < 0 || x + t->col_ofs > t->nc
235 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
236 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
238 printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
239 "table size (%d,%d)\n"),
240 x, t->col_ofs, x + t->col_ofs,
241 y1, t->row_ofs, y1 + t->row_ofs,
242 y2, t->row_ofs, y2 + t->row_ofs,
256 assert (y2 <= t->nr);
261 for (y = y1; y <= y2; y++)
262 t->rv[x + (t->cf + 1) * y] = style;
266 /* Draws a horizontal line above cells at vertical position Y from X1
267 to X2 inclusive in style STYLE, if style is not -1. */
269 tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
286 for (x = x1; x <= x2; x++)
287 t->rh[x + t->cf * y] = style;
291 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
292 lines of style F_H and vertical lines of style F_V. Fills the
293 interior of the box with horizontal lines of style I_H and vertical
294 lines of style I_V. Any of the line styles may be -1 to avoid
295 drawing those lines. This is distinct from 0, which draws a null
298 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
299 int x1, int y1, int x2, int y2)
304 if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc
305 || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= t->nc
306 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
307 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
309 printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
310 "in table size (%d,%d)\n"),
311 x1, t->col_ofs, x1 + t->col_ofs,
312 y1, t->row_ofs, y1 + t->row_ofs,
313 x2, t->col_ofs, x2 + t->col_ofs,
314 y2, t->row_ofs, y2 + t->row_ofs,
335 for (x = x1; x <= x2; x++)
337 t->rh[x + t->cf * y1] = f_h;
338 t->rh[x + t->cf * (y2 + 1)] = f_h;
344 for (y = y1; y <= y2; y++)
346 t->rv[x1 + (t->cf + 1) * y] = f_v;
347 t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
355 for (y = y1 + 1; y <= y2; y++)
359 for (x = x1; x <= x2; x++)
360 t->rh[x + t->cf * y] = i_h;
367 for (x = x1 + 1; x <= x2; x++)
371 for (y = y1; y <= y2; y++)
372 t->rv[x + (t->cf + 1) * y] = i_v;
377 /* Formats text TEXT and arguments ARGS as indicated in OPT in
378 TABLE's pool and returns the resultant string. */
379 static struct substring
380 text_format (struct tab_table *table, int opt, const char *text, va_list args)
382 assert (table != NULL && text != NULL);
384 return ss_cstr (opt & TAT_PRINTF
385 ? pool_vasprintf (table->container, text, args)
386 : pool_strdup (table->container, text));
389 /* Set the title of table T to TITLE, which is formatted as if
390 passed to printf(). */
392 tab_title (struct tab_table *t, const char *title, ...)
396 assert (t != NULL && title != NULL);
397 va_start (args, title);
398 t->title = xvasprintf (title, args);
402 /* Set DIM_FUNC as the dimension function for table T. */
404 tab_dim (struct tab_table *t, tab_dim_func *dim_func)
406 assert (t != NULL && t->dim == NULL);
410 /* Returns the natural width of column C in table T for driver D, that
411 is, the smallest width necessary to display all its cells without
412 wrapping. The width will be no larger than the page width minus
413 left and right rule widths. */
415 tab_natural_width (struct tab_table *t, struct outp_driver *d, int c)
419 assert (t != NULL && c >= 0 && c < t->nc);
423 for (width = r = 0; r < t->nr; r++)
425 struct outp_text text;
426 unsigned char opt = t->ct[c + r * t->cf];
429 if (opt & (TAB_JOIN | TAB_EMPTY))
432 text.string = t->cc[c + r * t->cf];
433 text.justification = OUTP_LEFT;
434 text.font = options_to_font (opt);
435 text.h = text.v = INT_MAX;
437 d->class->text_metrics (d, &text, &w, NULL);
445 /* FIXME: This is an ugly kluge to compensate for the fact
446 that we don't let joined cells contribute to column
448 width = d->prop_em_width * 8;
452 const int clamp = d->width - t->wrv[0] - t->wrv[t->nc];
461 /* Returns the natural height of row R in table T for driver D, that
462 is, the minimum height necessary to display the information in the
463 cell at the widths set for each column. */
465 tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
469 assert (t != NULL && r >= 0 && r < t->nr);
474 for (height = d->font_height, c = 0; c < t->nc; c++)
476 struct outp_text text;
477 unsigned char opt = t->ct[c + r * t->cf];
480 assert (t->w[c] != NOT_INT);
481 if (opt & (TAB_JOIN | TAB_EMPTY))
484 text.string = t->cc[c + r * t->cf];
485 text.justification = OUTP_LEFT;
486 text.font = options_to_font (opt);
489 d->class->text_metrics (d, &text, NULL, &h);
499 /* Callback function to set all columns and rows to their natural
500 dimensions. Not really meant to be called directly. */
502 tab_natural_dimensions (struct tab_table *t, struct outp_driver *d)
508 for (i = 0; i < t->nc; i++)
509 t->w[i] = tab_natural_width (t, d, i);
511 for (i = 0; i < t->nr; i++)
512 t->h[i] = tab_natural_height (t, d, i);
518 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
519 from V, displayed with format spec F. */
521 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
522 const union value *v, const struct fmt_spec *f)
526 assert (table != NULL && v != NULL && f != NULL);
528 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
529 || c + table->col_ofs >= table->nc
530 || r + table->row_ofs >= table->nr)
532 printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
534 c, table->col_ofs, c + table->col_ofs,
535 r, table->row_ofs, r + table->row_ofs,
536 table->nc, table->nr);
541 contents = pool_alloc (table->container, f->w);
542 table->cc[c + r * table->cf] = ss_buffer (contents, f->w);
543 table->ct[c + r * table->cf] = opt;
545 data_out (v, f, contents);
548 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
549 with NDEC decimal places. */
551 tab_float (struct tab_table *table, int c, int r, unsigned char opt,
552 double val, int w, int d)
558 union value double_value;
560 assert (table != NULL && w <= 40);
563 assert (c < table->nc);
565 assert (r < table->nr);
567 f = fmt_for_output (FMT_F, w, d);
570 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
571 || c + table->col_ofs >= table->nc
572 || r + table->row_ofs >= table->nr)
574 printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
576 c, table->col_ofs, c + table->col_ofs,
577 r, table->row_ofs, r + table->row_ofs,
578 table->nc, table->nr);
583 double_value.f = val;
584 data_out (&double_value, &f, buf);
587 while (isspace ((unsigned char) *cp) && cp < &buf[w])
589 f.w = w - (cp - buf);
591 contents = pool_alloc (table->container, f.w);
592 table->cc[c + r * table->cf] = ss_buffer (contents, f.w);
593 table->ct[c + r * table->cf] = opt;
594 memcpy (contents, cp, f.w);
597 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
600 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
604 assert (table != NULL && text != NULL);
608 assert (c < table->nc);
609 assert (r < table->nr);
613 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
614 || c + table->col_ofs >= table->nc
615 || r + table->row_ofs >= table->nr)
617 printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
619 c, table->col_ofs, c + table->col_ofs,
620 r, table->row_ofs, r + table->row_ofs,
621 table->nc, table->nr);
626 va_start (args, text);
627 table->cc[c + r * table->cf] = text_format (table, opt, text, args);
628 table->ct[c + r * table->cf] = opt;
632 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
633 options OPT to have text value TEXT. */
635 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
636 unsigned opt, const char *text, ...)
638 struct tab_joined_cell *j;
640 assert (table != NULL && text != NULL);
642 assert (x1 + table->col_ofs >= 0);
643 assert (y1 + table->row_ofs >= 0);
646 assert (y2 + table->row_ofs < table->nr);
647 assert (x2 + table->col_ofs < table->nc);
650 if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
651 || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
652 || x2 < x1 || x2 + table->col_ofs >= table->nc
653 || y2 < y2 || y2 + table->row_ofs >= table->nr)
655 printf ("tab_joint_text(): bad cell "
656 "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
657 x1, table->col_ofs, x1 + table->col_ofs,
658 y1, table->row_ofs, y1 + table->row_ofs,
659 x2, table->col_ofs, x2 + table->col_ofs,
660 y2, table->row_ofs, y2 + table->row_ofs,
661 table->nc, table->nr);
666 tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
668 j = pool_alloc (table->container, sizeof *j);
670 j->x1 = x1 + table->col_ofs;
671 j->y1 = y1 + table->row_ofs;
672 j->x2 = ++x2 + table->col_ofs;
673 j->y2 = ++y2 + table->row_ofs;
678 va_start (args, text);
679 j->contents = text_format (table, opt, text, args);
686 struct substring *cc = &table->cc[x1 + y1 * table->cf];
687 unsigned char *ct = &table->ct[x1 + y1 * table->cf];
688 const int ofs = table->cf - (x2 - x1);
692 for (y = y1; y < y2; y++)
696 for (x = x1; x < x2; x++)
698 *cc++ = ss_buffer ((char *) j, 0);
708 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
710 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
711 struct substring *string)
713 assert (table != NULL && string != NULL);
716 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
717 || c + table->col_ofs >= table->nc
718 || r + table->row_ofs >= table->nr)
720 printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
722 c, table->col_ofs, c + table->col_ofs,
723 r, table->row_ofs, r + table->row_ofs,
724 table->nc, table->nr);
729 table->cc[c + r * table->cf] = *string;
730 table->ct[c + r * table->cf] = opt;
735 /* Sets the widths of all the columns and heights of all the rows in
736 table T for driver D. */
738 nowrap_dim (struct tab_table *t, struct outp_driver *d)
740 t->w[0] = tab_natural_width (t, d, 0);
741 t->h[0] = d->font_height;
744 /* Sets the widths of all the columns and heights of all the rows in
745 table T for driver D. */
747 wrap_dim (struct tab_table *t, struct outp_driver *d)
749 t->w[0] = tab_natural_width (t, d, 0);
750 t->h[0] = tab_natural_height (t, d, 0);
753 /* Outputs text BUF as a table with a single cell having cell options
754 OPTIONS, which is a combination of the TAB_* and TAT_*
757 tab_output_text (int options, const char *buf, ...)
759 struct tab_table *t = tab_create (1, 1, 0);
760 char *tmp_buf = NULL;
762 if (options & TAT_PRINTF)
766 va_start (args, buf);
767 buf = tmp_buf = xvasprintf (buf, args);
771 tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
772 tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
773 tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim);
779 /* Set table flags to FLAGS. */
781 tab_flags (struct tab_table *t, unsigned flags)
787 /* Easy, type-safe way to submit a tab table to som. */
789 tab_submit (struct tab_table *t)
794 s.class = &tab_table_class;
803 /* Set table row and column offsets for all functions that affect
806 tab_offset (struct tab_table *t, int col, int row)
812 if (row < -1 || row > t->nr)
814 printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
817 if (col < -1 || col > t->nc)
819 printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
825 diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
827 diff += (col - t->col_ofs), t->col_ofs = col;
833 /* Increment the row offset by one. If the table is too small,
834 increase its size. */
836 tab_next_row (struct tab_table *t)
841 if (++t->row_ofs >= t->nr)
842 tab_realloc (t, -1, t->nr * 4 / 3);
845 static struct tab_table *t;
846 static struct outp_driver *d;
849 /* Set the current table to TABLE. */
851 tabi_table (struct som_entity *table)
853 assert (table != NULL);
854 assert (table->type == SOM_TABLE);
857 tab_offset (t, 0, 0);
859 assert (t->w == NULL && t->h == NULL);
860 t->w = pool_nalloc (t->container, t->nc, sizeof *t->w);
861 t->h = pool_nalloc (t->container, t->nr, sizeof *t->h);
862 t->hrh = pool_nmalloc (t->container, t->nr + 1, sizeof *t->hrh);
863 t->wrv = pool_nmalloc (t->container, t->nc + 1, sizeof *t->wrv);
866 /* Returns the line style to use for spacing purposes for a rule
867 of the given TYPE. */
868 static enum outp_line_style
869 rule_to_spacing_type (unsigned char type)
877 return OUTP_L_SINGLE;
879 return OUTP_L_DOUBLE;
885 /* Set the current output device to DRIVER. */
887 tabi_driver (struct outp_driver *driver)
892 assert (driver != NULL);
895 /* Figure out sizes of rules. */
896 for (r = 0; r <= t->nr; r++)
899 for (c = 0; c < t->nc; c++)
901 unsigned char rh = t->rh[c + r * t->cf];
902 int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
909 for (c = 0; c <= t->nc; c++)
912 for (r = 0; r < t->nr; r++)
914 unsigned char *rv = &t->rv[c + r * (t->cf + 1)];
916 if (*rv == UCHAR_MAX)
917 *rv = c != 0 && c != t->nc ? TAL_GAP : TAL_0;
918 w = driver->vert_line_width[rule_to_spacing_type (*rv)];
926 for (i = 0; i < t->nr; i++)
928 for (i = 0; i < t->nc; i++)
932 assert (t->dim != NULL);
939 for (i = 0; i < t->nr; i++)
943 printf ("Table row %d height not initialized.\n", i);
946 assert (t->h[i] > 0);
949 for (i = 0; i < t->nc; i++)
953 printf ("Table column %d width not initialized.\n", i);
956 assert (t->w[i] > 0);
961 /* Add up header sizes. */
962 for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
963 t->wl += t->w[i] + t->wrv[i + 1];
964 for (i = 0, t->ht = t->hrh[0]; i < t->t; i++)
965 t->ht += t->h[i] + t->hrh[i + 1];
966 for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++)
967 t->wr += t->w[i] + t->wrv[i + 1];
968 for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
969 t->hb += t->h[i] + t->hrh[i + 1];
972 if (!(t->flags & SOMF_NO_TITLE))
973 t->ht += d->font_height;
976 /* Return the number of columns and rows in the table into N_COLUMNS
977 and N_ROWS, respectively. */
979 tabi_count (int *n_columns, int *n_rows)
981 assert (n_columns != NULL && n_rows != NULL);
986 static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual);
988 /* Return the horizontal and vertical size of the entire table,
989 including headers, for the current output device, into HORIZ and
992 tabi_area (int *horiz, int *vert)
994 assert (horiz != NULL && vert != NULL);
999 for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
1000 c < t->nc - t->r; c++)
1001 w += t->w[c] + t->wrv[c];
1007 for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
1008 r < t->nr - t->b; r++)
1009 h += t->h[r] + t->hrh[r];
1014 /* Return the column style for this table into STYLE. */
1016 tabi_columns (int *style)
1018 assert (style != NULL);
1019 *style = t->col_style;
1022 /* Return the number of header rows/columns on the left, right, top,
1023 and bottom sides into HL, HR, HT, and HB, respectively. */
1025 tabi_headers (int *hl, int *hr, int *ht, int *hb)
1027 assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL);
1034 /* Determines the number of rows or columns (including appropriate
1035 headers), depending on CUMTYPE, that will fit into the space
1036 specified. Takes rows/columns starting at index START and attempts
1037 to fill up available space MAX. Returns in END the index of the
1038 last row/column plus one; returns in ACTUAL the actual amount of
1039 space the selected rows/columns (including appropriate headers)
1042 tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
1049 assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1050 if (cumtype == SOM_ROWS)
1052 assert (start >= 0 && start < t->nr);
1055 r = &t->hrh[start + 1];
1056 total = t->ht + t->hb;
1060 assert (start >= 0 && start < t->nc);
1063 r = &t->wrv[start + 1];
1064 total = t->wl + t->wr;
1080 for (x = start + 1; x < n; x++)
1082 int amt = *d++ + *r++;
1100 /* Return flags set for the current table into FLAGS. */
1102 tabi_flags (unsigned *flags)
1104 assert (flags != NULL);
1108 /* Returns true if the table will fit in the given page WIDTH,
1111 tabi_fits_width (int width)
1115 for (i = t->l; i < t->nc - t->r; i++)
1116 if (t->wl + t->wr + t->w[i] > width)
1122 /* Returns true if the table will fit in the given page LENGTH,
1125 tabi_fits_length (int length)
1129 for (i = t->t; i < t->nr - t->b; i++)
1130 if (t->ht + t->hb + t->h[i] > length)
1136 /* Sets the number of header rows/columns on the left, right, top,
1137 and bottom sides to HL, HR, HT, and HB, respectively. */
1139 tabi_set_headers (int hl, int hr, int ht, int hb)
1147 /* Render title for current table, with major index X and minor index
1148 Y. Y may be zero, or X and Y may be zero, but X should be nonzero
1151 tabi_title (int x, int y)
1156 if (t->flags & SOMF_NO_TITLE)
1159 cp = spprintf (buf, "%d.%d", table_num, subtable_num);
1161 cp = spprintf (cp, "(%d:%d)", x, y);
1163 cp = spprintf (cp, "(%d)", x);
1164 if (command_name != NULL)
1165 cp = spprintf (cp, " %s", command_name);
1166 cp = stpcpy (cp, ". ");
1167 if (t->title != NULL)
1169 size_t length = strlen (t->title);
1170 memcpy (cp, t->title, length);
1176 struct outp_text text;
1178 text.font = OUTP_PROPORTIONAL;
1179 text.justification = OUTP_LEFT;
1180 text.string = ss_buffer (buf, cp - buf);
1182 text.v = d->font_height;
1185 d->class->text_draw (d, &text);
1189 static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
1191 /* Renders columns C0...C1, plus headers, of rows R0...R1,
1192 at the given vertical position Y.
1193 C0 and C1 count vertical rules as columns,
1194 but R0 and R1 do not count horizontal rules as rows.
1195 Returns the vertical position after rendering. */
1197 render_rows (int y, int c0, int c1, int r0, int r1)
1200 for (r = r0; r < r1; r++)
1203 x = render_strip (x, y, r, 0, t->l * 2 + 1, r0, r1);
1204 x = render_strip (x, y, r, c0 * 2 + 1, c1 * 2, r0, r1);
1205 x = render_strip (x, y, r, (t->nc - t->r) * 2, t->nc * 2 + 1, r0, r1);
1206 y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2];
1211 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1212 current position on the current output device. */
1214 tabi_render (int c0, int r0, int c1, int r1)
1221 if (!(t->flags & SOMF_NO_TITLE))
1222 y += d->font_height;
1224 y = render_rows (y, c0, c1, 0, t->t * 2 + 1);
1225 y = render_rows (y, c0, c1, r0 * 2 + 1, r1 * 2);
1226 y = render_rows (y, c0, c1, (t->nr - t->b) * 2, t->nr * 2 + 1);
1229 const struct som_table_class tab_table_class =
1255 static enum outp_justification
1256 translate_justification (unsigned int opt)
1258 switch (opt & TAB_ALIGN_MASK)
1271 /* Returns the line style to use for drawing a rule of the given
1273 static enum outp_line_style
1274 rule_to_draw_type (unsigned char type)
1282 return OUTP_L_SINGLE;
1284 return OUTP_L_DOUBLE;
1290 /* Returns the horizontal rule at the given column and row. */
1292 get_hrule (int c, int r)
1294 return t->rh[c + r * t->cf];
1297 /* Returns the vertical rule at the given column and row. */
1299 get_vrule (int c, int r)
1301 return t->rv[c + r * (t->cf + 1)];
1304 /* Renders the horizontal rule at the given column and row
1305 at (X,Y) on the page. */
1307 render_horz_rule (int x, int y, int c, int r)
1309 enum outp_line_style style = rule_to_draw_type (get_hrule (c, r));
1310 if (style != OUTP_L_NONE)
1311 d->class->line (d, x, y, x + t->w[c], y + t->hrh[r],
1312 OUTP_L_NONE, style, OUTP_L_NONE, style);
1315 /* Renders the vertical rule at the given column and row
1316 at (X,Y) on the page. */
1318 render_vert_rule (int x, int y, int c, int r)
1320 enum outp_line_style style = rule_to_draw_type (get_vrule (c, r));
1321 if (style != OUTP_L_NONE)
1322 d->class->line (d, x, y, x + t->wrv[c], y + t->h[r],
1323 style, OUTP_L_NONE, style, OUTP_L_NONE);
1326 /* Renders the rule intersection at the given column and row
1327 at (X,Y) on the page. */
1329 render_rule_intersection (int x, int y, int c, int r)
1331 /* Bounds of intersection. */
1334 int x1 = x + t->wrv[c];
1335 int y1 = y + t->hrh[r];
1337 /* Lines on each side of intersection. */
1338 int top = r > 0 ? get_vrule (c, r - 1) : TAL_0;
1339 int left = c > 0 ? get_hrule (c - 1, r) : TAL_0;
1340 int bottom = r < t->nr ? get_vrule (c, r) : TAL_0;
1341 int right = c < t->nc ? get_hrule (c, r) : TAL_0;
1343 /* Output style for each line. */
1344 enum outp_line_style o_top = rule_to_draw_type (top);
1345 enum outp_line_style o_left = rule_to_draw_type (left);
1346 enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1347 enum outp_line_style o_right = rule_to_draw_type (right);
1349 if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1350 || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1351 d->class->line (d, x0, y0, x1, y1, o_top, o_left, o_bottom, o_right);
1354 /* Returns the width of columns C1...C2 exclusive,
1355 including interior but not exterior rules. */
1357 strip_width (int c1, int c2)
1362 for (c = c1; c < c2; c++)
1363 width += t->w[c] + t->wrv[c + 1];
1365 width -= t->wrv[c2];
1369 /* Returns the height of rows R1...R2 exclusive,
1370 including interior but not exterior rules. */
1372 strip_height (int r1, int r2)
1377 for (r = r1; r < r2; r++)
1378 height += t->h[r] + t->hrh[r + 1];
1380 height -= t->hrh[r2];
1384 /* Renders the cell at the given column and row at (X,Y) on the
1385 page. Also renders joined cells that extend as far to the
1386 right as C1 and as far down as R1. */
1388 render_cell (int x, int y, int c, int r, int c1, int r1)
1390 const int index = c + (r * t->cf);
1391 unsigned char type = t->ct[index];
1392 struct substring *content = &t->cc[index];
1394 if (!(type & TAB_JOIN))
1396 if (!(type & TAB_EMPTY))
1398 struct outp_text text;
1399 text.font = options_to_font (type);
1400 text.justification = translate_justification (type);
1401 text.string = *content;
1406 d->class->text_draw (d, &text);
1411 struct tab_joined_cell *j
1412 = (struct tab_joined_cell *) ss_data (*content);
1414 if (j->hit != tab_hit)
1418 if (j->x1 == c && j->y1 == r)
1420 struct outp_text text;
1421 text.font = options_to_font (type);
1422 text.justification = translate_justification (type);
1423 text.string = j->contents;
1426 text.h = strip_width (j->x1, MIN (j->x2, c1));
1427 text.v = strip_height (j->y1, MIN (j->y2, r1));
1428 d->class->text_draw (d, &text);
1434 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1435 on row R, at (X,Y). Returns X position after rendering.
1436 Also renders joined cells that extend beyond that strip,
1437 cropping them to lie within rendering region (C0,R0)-(C1,R1).
1438 C0 and C1 count vertical rules as columns.
1439 R counts horizontal rules as rows, but R0 and R1 do not. */
1441 render_strip (int x, int y, int r, int c0, int c1, int r0 UNUSED, int r1)
1445 for (c = c0; c < c1; c++)
1449 render_cell (x, y, c / 2, r / 2, c1 / 2, r1);
1451 render_horz_rule (x, y, c / 2, r / 2);
1457 render_vert_rule (x, y, c / 2, r / 2);
1459 render_rule_intersection (x, y, c / 2, r / 2);
1466 /* Sets COMMAND_NAME as the name of the current command,
1467 for embedding in output. */
1469 tab_set_command_name (const char *command_name_)
1471 free (command_name);
1472 command_name = command_name_ ? xstrdup (command_name_) : NULL;