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/message.h>
27 #include <libpspp/alloc.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->hrh = pool_nmalloc (t->container, nr + 1, sizeof *t->hrh);
76 memset (t->hrh, 0, sizeof *t->hrh * (nr + 1));
78 t->rv = pool_nmalloc (t->container, nr, nc + 1);
79 memset (t->rv, UCHAR_MAX, nr * (nc + 1));
81 t->wrv = pool_nmalloc (t->container, nc + 1, sizeof *t->wrv);
82 memset (t->wrv, 0, sizeof *t->wrv * (nc + 1));
86 t->col_ofs = t->row_ofs = 0;
91 /* Destroys table T. */
93 tab_destroy (struct tab_table *t)
96 pool_destroy (t->container);
99 /* Sets the width and height of a table, in columns and rows,
100 respectively. Use only to reduce the size of a table, since it
101 does not change the amount of allocated memory. */
103 tab_resize (struct tab_table *t, int nc, int nr)
108 assert (nc + t->col_ofs <= t->cf);
109 t->nc = nc + t->col_ofs;
113 assert (nr + t->row_ofs <= t->nr);
114 t->nr = nr + t->row_ofs;
118 /* Changes either or both dimensions of a table. Consider using the
119 above routine instead if it won't waste a lot of space.
121 Changing the number of columns in a table is particularly expensive
122 in space and time. Avoid doing such. FIXME: In fact, transferring
123 of rules isn't even implemented yet. */
125 tab_realloc (struct tab_table *t, int nc, int nr)
133 tab_offset (t, 0, 0);
140 assert (nc == t->nc);
144 int mr1 = min (nr, t->nr);
145 int mc1 = min (nc, t->nc);
147 struct fixed_string *new_cc;
148 unsigned char *new_ct;
151 new_cc = pool_nmalloc (t->container, nr * nc, sizeof *new_cc);
152 new_ct = pool_malloc (t->container, nr * nc);
153 for (r = 0; r < mr1; r++)
155 memcpy (&new_cc[r * nc], &t->cc[r * t->nc], mc1 * sizeof *t->cc);
156 memcpy (&new_ct[r * nc], &t->ct[r * t->nc], mc1);
157 memset (&new_ct[r * nc + t->nc], TAB_EMPTY, nc - t->nc);
159 pool_free (t->container, t->cc);
160 pool_free (t->container, t->ct);
165 else if (nr != t->nr)
167 t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc);
168 t->ct = pool_realloc (t->container, t->ct, nr * nc);
170 t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
171 t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
175 memset (&t->rh[nc * (t->nr + 1)], TAL_0, (nr - t->nr) * nc);
176 memset (&t->rv[(nc + 1) * t->nr], UCHAR_MAX,
177 (nr - t->nr) * (nc + 1));
181 memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr));
187 tab_offset (t, co, ro);
190 /* Sets the number of header rows on each side of TABLE to L on the
191 left, R on the right, T on the top, B on the bottom. Header rows
192 are repeated when a table is broken across multiple columns or
195 tab_headers (struct tab_table *table, int l, int r, int t, int b)
197 assert (table != NULL);
198 assert (l < table->nc);
199 assert (r < table->nc);
200 assert (t < table->nr);
201 assert (b < table->nr);
210 /* Set up table T so that, when it is an appropriate size, it will be
211 displayed across the page in columns.
213 STYLE is a TAB_COL_* constant. GROUP is the number of rows to take
216 tab_columns (struct tab_table *t, int style, int group)
219 t->col_style = style;
220 t->col_group = group;
225 /* Draws a vertical line to the left of cells at horizontal position X
226 from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
228 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
233 if (x + t->col_ofs < 0 || x + t->col_ofs > t->nc
234 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
235 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
237 printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
238 "table size (%d,%d)\n"),
239 x, t->col_ofs, x + t->col_ofs,
240 y1, t->row_ofs, y1 + t->row_ofs,
241 y2, t->row_ofs, y2 + t->row_ofs,
255 assert (y2 <= t->nr);
260 for (y = y1; y <= y2; y++)
261 t->rv[x + (t->cf + 1) * y] = style;
265 /* Draws a horizontal line above cells at vertical position Y from X1
266 to X2 inclusive in style STYLE, if style is not -1. */
268 tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
285 for (x = x1; x <= x2; x++)
286 t->rh[x + t->cf * y] = style;
290 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
291 lines of style F_H and vertical lines of style F_V. Fills the
292 interior of the box with horizontal lines of style I_H and vertical
293 lines of style I_V. Any of the line styles may be -1 to avoid
294 drawing those lines. This is distinct from 0, which draws a null
297 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
298 int x1, int y1, int x2, int y2)
303 if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc
304 || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= t->nc
305 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
306 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
308 printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
309 "in table size (%d,%d)\n"),
310 x1, t->col_ofs, x1 + t->col_ofs,
311 y1, t->row_ofs, y1 + t->row_ofs,
312 x2, t->col_ofs, x2 + t->col_ofs,
313 y2, t->row_ofs, y2 + t->row_ofs,
334 for (x = x1; x <= x2; x++)
336 t->rh[x + t->cf * y1] = f_h;
337 t->rh[x + t->cf * (y2 + 1)] = f_h;
343 for (y = y1; y <= y2; y++)
345 t->rv[x1 + (t->cf + 1) * y] = f_v;
346 t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
354 for (y = y1 + 1; y <= y2; y++)
358 for (x = x1; x <= x2; x++)
359 t->rh[x + t->cf * y] = i_h;
366 for (x = x1 + 1; x <= x2; x++)
370 for (y = y1; y <= y2; y++)
371 t->rv[x + (t->cf + 1) * y] = i_v;
376 /* Formats text TEXT and arguments ARGS as indicated in OPT and sets
377 the resultant string into S in TABLE's pool. */
379 text_format (struct tab_table *table, int opt, const char *text, va_list args,
380 struct fixed_string *s)
384 assert (table != NULL && text != NULL && s != NULL);
386 if (opt & TAT_PRINTF)
387 text = tmp = xvasprintf (text, args);
389 ls_create_buffer (s, text, strlen (text));
390 pool_register (table->container, free, s->string);
395 /* Set the title of table T to TITLE, which is formatted as if
396 passed to printf(). */
398 tab_title (struct tab_table *t, const char *title, ...)
402 assert (t != NULL && title != NULL);
403 va_start (args, title);
404 text_format (t, TAT_PRINTF, title, args, &t->title);
408 /* Set DIM_FUNC as the dimension function for table T. */
410 tab_dim (struct tab_table *t, tab_dim_func *dim_func)
412 assert (t != NULL && t->dim == NULL);
416 /* Returns the natural width of column C in table T for driver D, that
417 is, the smallest width necessary to display all its cells without
418 wrapping. The width will be no larger than the page width minus
419 left and right rule widths. */
421 tab_natural_width (struct tab_table *t, struct outp_driver *d, int c)
425 assert (t != NULL && c >= 0 && c < t->nc);
429 for (width = r = 0; r < t->nr; r++)
431 struct outp_text text;
432 unsigned char opt = t->ct[c + r * t->cf];
435 if (opt & (TAB_JOIN | TAB_EMPTY))
438 text.string = t->cc[c + r * t->cf];
439 assert (!ls_null_p (&text.string));
440 text.justification = OUTP_LEFT;
441 text.font = options_to_font (opt);
442 text.h = text.v = INT_MAX;
444 d->class->text_metrics (d, &text, &w, NULL);
452 width = d->prop_em_width * 8;
454 printf ("warning: table column %d contains no data.\n", c);
459 const int clamp = d->width - t->wrv[0] - t->wrv[t->nc];
468 /* Returns the natural height of row R in table T for driver D, that
469 is, the minimum height necessary to display the information in the
470 cell at the widths set for each column. */
472 tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
476 assert (t != NULL && r >= 0 && r < t->nr);
481 for (height = d->font_height, c = 0; c < t->nc; c++)
483 struct outp_text text;
484 unsigned char opt = t->ct[c + r * t->cf];
487 assert (t->w[c] != NOT_INT);
488 if (opt & (TAB_JOIN | TAB_EMPTY))
491 text.string = t->cc[c + r * t->cf];
492 assert (!ls_null_p (&text.string));
493 text.justification = OUTP_LEFT;
494 text.font = options_to_font (opt);
497 d->class->text_metrics (d, &text, NULL, &h);
507 /* Callback function to set all columns and rows to their natural
508 dimensions. Not really meant to be called directly. */
510 tab_natural_dimensions (struct tab_table *t, struct outp_driver *d)
516 for (i = 0; i < t->nc; i++)
517 t->w[i] = tab_natural_width (t, d, i);
519 for (i = 0; i < t->nr; i++)
520 t->h[i] = tab_natural_height (t, d, i);
526 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
527 from V, displayed with format spec F. */
529 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
530 const union value *v, const struct fmt_spec *f)
534 assert (table != NULL && v != NULL && f != NULL);
536 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
537 || c + table->col_ofs >= table->nc
538 || r + table->row_ofs >= table->nr)
540 printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
542 c, table->col_ofs, c + table->col_ofs,
543 r, table->row_ofs, r + table->row_ofs,
544 table->nc, table->nr);
549 contents = pool_alloc (table->container, f->w);
550 ls_init (&table->cc[c + r * table->cf], contents, f->w);
551 table->ct[c + r * table->cf] = opt;
553 data_out (contents, f, v);
556 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
557 with NDEC decimal places. */
559 tab_float (struct tab_table *table, int c, int r, unsigned char opt,
560 double val, int w, int d)
566 union value double_value;
568 assert (table != NULL && w <= 40);
571 assert (c < table->nc);
573 assert (r < table->nr);
575 f = make_output_format (FMT_F, w, d);
578 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
579 || c + table->col_ofs >= table->nc
580 || r + table->row_ofs >= table->nr)
582 printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
584 c, table->col_ofs, c + table->col_ofs,
585 r, table->row_ofs, r + table->row_ofs,
586 table->nc, table->nr);
591 double_value.f = val;
592 data_out (buf, &f, &double_value);
595 while (isspace ((unsigned char) *cp) && cp < &buf[w])
597 f.w = w - (cp - buf);
599 contents = pool_alloc (table->container, f.w);
600 ls_init (&table->cc[c + r * table->cf], contents, f.w);
601 table->ct[c + r * table->cf] = opt;
602 memcpy (contents, cp, f.w);
605 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
608 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
612 assert (table != NULL && text != NULL);
616 assert (c < table->nc);
617 assert (r < table->nr);
621 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
622 || c + table->col_ofs >= table->nc
623 || r + table->row_ofs >= table->nr)
625 printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
627 c, table->col_ofs, c + table->col_ofs,
628 r, table->row_ofs, r + table->row_ofs,
629 table->nc, table->nr);
634 va_start (args, text);
635 text_format (table, opt, text, args, &table->cc[c + r * table->cf]);
636 table->ct[c + r * table->cf] = opt;
640 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
641 options OPT to have text value TEXT. */
643 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
644 unsigned opt, const char *text, ...)
646 struct tab_joined_cell *j;
648 assert (table != NULL && text != NULL);
650 assert (x1 + table->col_ofs >= 0);
651 assert (y1 + table->row_ofs >= 0);
654 assert (y2 + table->row_ofs < table->nr);
655 assert (x2 + table->col_ofs < table->nc);
658 if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
659 || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
660 || x2 < x1 || x2 + table->col_ofs >= table->nc
661 || y2 < y2 || y2 + table->row_ofs >= table->nr)
663 printf ("tab_joint_text(): bad cell "
664 "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
665 x1, table->col_ofs, x1 + table->col_ofs,
666 y1, table->row_ofs, y1 + table->row_ofs,
667 x2, table->col_ofs, x2 + table->col_ofs,
668 y2, table->row_ofs, y2 + table->row_ofs,
669 table->nc, table->nr);
674 tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
676 j = pool_alloc (table->container, sizeof *j);
678 j->x1 = x1 + table->col_ofs;
679 j->y1 = y1 + table->row_ofs;
680 j->x2 = ++x2 + table->col_ofs;
681 j->y2 = ++y2 + table->row_ofs;
686 va_start (args, text);
687 text_format (table, opt, text, args, &j->contents);
694 struct fixed_string *cc = &table->cc[x1 + y1 * table->cf];
695 unsigned char *ct = &table->ct[x1 + y1 * table->cf];
696 const int ofs = table->cf - (x2 - x1);
700 for (y = y1; y < y2; y++)
704 for (x = x1; x < x2; x++)
706 ls_init (cc++, (char *) j, 0);
716 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
718 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
719 struct fixed_string *string)
721 assert (table != NULL && string != NULL);
724 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
725 || c + table->col_ofs >= table->nc
726 || r + table->row_ofs >= table->nr)
728 printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
730 c, table->col_ofs, c + table->col_ofs,
731 r, table->row_ofs, r + table->row_ofs,
732 table->nc, table->nr);
737 table->cc[c + r * table->cf] = *string;
738 table->ct[c + r * table->cf] = opt;
743 /* Sets the widths of all the columns and heights of all the rows in
744 table T for driver D. */
746 nowrap_dim (struct tab_table *t, struct outp_driver *d)
748 t->w[0] = tab_natural_width (t, d, 0);
749 t->h[0] = d->font_height;
752 /* Sets the widths of all the columns and heights of all the rows in
753 table T for driver D. */
755 wrap_dim (struct tab_table *t, struct outp_driver *d)
757 t->w[0] = tab_natural_width (t, d, 0);
758 t->h[0] = tab_natural_height (t, d, 0);
761 /* Outputs text BUF as a table with a single cell having cell options
762 OPTIONS, which is a combination of the TAB_* and TAT_*
765 tab_output_text (int options, const char *buf, ...)
767 struct tab_table *t = tab_create (1, 1, 0);
768 char *tmp_buf = NULL;
770 if (options & TAT_PRINTF)
774 va_start (args, buf);
775 buf = tmp_buf = xvasprintf (buf, args);
779 tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
780 tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
781 tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim);
787 /* Set table flags to FLAGS. */
789 tab_flags (struct tab_table *t, unsigned flags)
795 /* Easy, type-safe way to submit a tab table to som. */
797 tab_submit (struct tab_table *t)
802 s.class = &tab_table_class;
811 /* Set table row and column offsets for all functions that affect
814 tab_offset (struct tab_table *t, int col, int row)
820 if (row < -1 || row >= t->nr)
822 printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
825 if (col < -1 || col >= t->nc)
827 printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
833 diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
835 diff += (col - t->col_ofs), t->col_ofs = col;
841 /* Increment the row offset by one. If the table is too small,
842 increase its size. */
844 tab_next_row (struct tab_table *t)
849 if (++t->row_ofs >= t->nr)
850 tab_realloc (t, -1, t->nr * 4 / 3);
853 static struct tab_table *t;
854 static struct outp_driver *d;
857 /* Set the current table to TABLE. */
859 tabi_table (struct som_entity *table)
861 assert (table != NULL);
862 assert (table->type == SOM_TABLE);
865 tab_offset (t, 0, 0);
867 assert (t->w == NULL && t->h == NULL);
868 t->w = pool_nalloc (t->container, t->nc, sizeof *t->w);
869 t->h = pool_nalloc (t->container, t->nr, sizeof *t->h);
872 /* Returns the line style to use for spacing purposes for a rule
873 of the given TYPE. */
874 static enum outp_line_style
875 rule_to_spacing_type (unsigned char type)
883 return OUTP_L_SINGLE;
885 return OUTP_L_DOUBLE;
891 /* Set the current output device to DRIVER. */
893 tabi_driver (struct outp_driver *driver)
898 assert (driver != NULL);
901 /* Figure out sizes of rules. */
902 for (r = 0; r <= t->nr; r++)
905 for (c = 0; c < t->nc; c++)
907 unsigned char rh = t->rh[c + r * t->cf];
908 int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
915 for (c = 0; c <= t->nc; c++)
918 for (r = 0; r < t->nr; r++)
920 unsigned char *rv = &t->rv[c + r * (t->cf + 1)];
922 if (*rv == UCHAR_MAX)
923 *rv = c != 0 && c != t->nc ? TAL_GAP : TAL_0;
924 w = driver->vert_line_width[rule_to_spacing_type (*rv)];
932 for (i = 0; i < t->nr; i++)
934 for (i = 0; i < t->nc; i++)
938 assert (t->dim != NULL);
945 for (i = 0; i < t->nr; i++)
949 printf ("Table row %d height not initialized.\n", i);
952 assert (t->h[i] > 0);
955 for (i = 0; i < t->nc; i++)
959 printf ("Table column %d width not initialized.\n", i);
962 assert (t->w[i] > 0);
967 /* Add up header sizes. */
968 for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
969 t->wl += t->w[i] + t->wrv[i + 1];
970 for (i = 0, t->ht = t->hrh[0]; i < t->t; i++)
971 t->ht += t->h[i] + t->hrh[i + 1];
972 for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++)
973 t->wr += t->w[i] + t->wrv[i + 1];
974 for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
975 t->hb += t->h[i] + t->hrh[i + 1];
978 if (!(t->flags & SOMF_NO_TITLE))
979 t->ht += d->font_height;
982 /* Return the number of columns and rows in the table into N_COLUMNS
983 and N_ROWS, respectively. */
985 tabi_count (int *n_columns, int *n_rows)
987 assert (n_columns != NULL && n_rows != NULL);
992 static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual);
994 /* Return the horizontal and vertical size of the entire table,
995 including headers, for the current output device, into HORIZ and
998 tabi_area (int *horiz, int *vert)
1000 assert (horiz != NULL && vert != NULL);
1005 for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
1006 c < t->nc - t->r; c++)
1007 w += t->w[c] + t->wrv[c];
1013 for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
1014 r < t->nr - t->b; r++)
1015 h += t->h[r] + t->hrh[r];
1020 /* Return the column style for this table into STYLE. */
1022 tabi_columns (int *style)
1024 assert (style != NULL);
1025 *style = t->col_style;
1028 /* Return the number of header rows/columns on the left, right, top,
1029 and bottom sides into HL, HR, HT, and HB, respectively. */
1031 tabi_headers (int *hl, int *hr, int *ht, int *hb)
1033 assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL);
1040 /* Determines the number of rows or columns (including appropriate
1041 headers), depending on CUMTYPE, that will fit into the space
1042 specified. Takes rows/columns starting at index START and attempts
1043 to fill up available space MAX. Returns in END the index of the
1044 last row/column plus one; returns in ACTUAL the actual amount of
1045 space the selected rows/columns (including appropriate headers)
1048 tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
1055 assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1056 if (cumtype == SOM_ROWS)
1058 assert (start >= 0 && start < t->nr);
1061 r = &t->hrh[start + 1];
1062 total = t->ht + t->hb;
1066 assert (start >= 0 && start < t->nc);
1069 r = &t->wrv[start + 1];
1070 total = t->wl + t->wr;
1086 for (x = start + 1; x < n; x++)
1088 int amt = *d++ + *r++;
1106 /* Return flags set for the current table into FLAGS. */
1108 tabi_flags (unsigned *flags)
1110 assert (flags != NULL);
1114 /* Returns true if the table will fit in the given page WIDTH,
1117 tabi_fits_width (int width)
1121 for (i = t->l; i < t->nc - t->r; i++)
1122 if (t->wl + t->wr + t->w[i] > width)
1128 /* Returns true if the table will fit in the given page LENGTH,
1131 tabi_fits_length (int length)
1135 for (i = t->t; i < t->nr - t->b; i++)
1136 if (t->ht + t->hb + t->h[i] > length)
1142 /* Sets the number of header rows/columns on the left, right, top,
1143 and bottom sides to HL, HR, HT, and HB, respectively. */
1145 tabi_set_headers (int hl, int hr, int ht, int hb)
1153 /* Render title for current table, with major index X and minor index
1154 Y. Y may be zero, or X and Y may be zero, but X should be nonzero
1157 tabi_title (int x, int y)
1162 if (t->flags & SOMF_NO_TITLE)
1165 cp = spprintf (buf, "%d.%d", table_num, subtable_num);
1167 cp = spprintf (cp, "(%d:%d)", x, y);
1169 cp = spprintf (cp, "(%d)", x);
1170 if (command_name != NULL)
1171 cp = spprintf (cp, " %s", command_name);
1172 cp = stpcpy (cp, ". ");
1173 if (!ls_empty_p (&t->title))
1175 memcpy (cp, ls_c_str (&t->title), ls_length (&t->title));
1176 cp += ls_length (&t->title);
1181 struct outp_text text;
1183 text.font = OUTP_PROPORTIONAL;
1184 text.justification = OUTP_LEFT;
1185 ls_init (&text.string, buf, cp - buf);
1187 text.v = d->font_height;
1190 d->class->text_draw (d, &text);
1194 static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
1196 /* Renders columns C0...C1, plus headers, of rows R0...R1,
1197 at the given vertical position Y.
1198 C0 and C1 count vertical rules as columns,
1199 but R0 and R1 do not count horizontal rules as rows.
1200 Returns the vertical position after rendering. */
1202 render_rows (int y, int c0, int c1, int r0, int r1)
1205 for (r = r0; r < r1; r++)
1208 x = render_strip (x, y, r, 0, t->l * 2 + 1, r0, r1);
1209 x = render_strip (x, y, r, c0 * 2 + 1, c1 * 2, r0, r1);
1210 x = render_strip (x, y, r, (t->nc - t->r) * 2, t->nc * 2 + 1, r0, r1);
1211 y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2];
1216 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1217 current position on the current output device. */
1219 tabi_render (int c0, int r0, int c1, int r1)
1226 if (!(t->flags & SOMF_NO_TITLE))
1227 y += d->font_height;
1229 y = render_rows (y, c0, c1, 0, t->t * 2 + 1);
1230 y = render_rows (y, c0, c1, r0 * 2 + 1, r1 * 2);
1231 y = render_rows (y, c0, c1, (t->nr - t->b) * 2, t->nr * 2 + 1);
1234 struct som_table_class tab_table_class =
1260 static enum outp_justification
1261 translate_justification (unsigned int opt)
1263 switch (opt & TAB_ALIGN_MASK)
1276 /* Returns the line style to use for drawing a rule of the given
1278 static enum outp_line_style
1279 rule_to_draw_type (unsigned char type)
1287 return OUTP_L_SINGLE;
1289 return OUTP_L_DOUBLE;
1295 /* Returns the horizontal rule at the given column and row. */
1297 get_hrule (int c, int r)
1299 return t->rh[c + r * t->cf];
1302 /* Returns the vertical rule at the given column and row. */
1304 get_vrule (int c, int r)
1306 return t->rv[c + r * (t->cf + 1)];
1309 /* Renders the horizontal rule at the given column and row
1310 at (X,Y) on the page. */
1312 render_horz_rule (int x, int y, int c, int r)
1314 enum outp_line_style style = rule_to_draw_type (get_hrule (c, r));
1315 if (style != OUTP_L_NONE)
1316 d->class->line (d, x, y, x + t->w[c], y + t->hrh[r],
1317 OUTP_L_NONE, style, OUTP_L_NONE, style);
1320 /* Renders the vertical rule at the given column and row
1321 at (X,Y) on the page. */
1323 render_vert_rule (int x, int y, int c, int r)
1325 enum outp_line_style style = rule_to_draw_type (get_vrule (c, r));
1326 if (style != OUTP_L_NONE)
1327 d->class->line (d, x, y, x + t->wrv[c], y + t->h[r],
1328 style, OUTP_L_NONE, style, OUTP_L_NONE);
1331 /* Renders the rule intersection at the given column and row
1332 at (X,Y) on the page. */
1334 render_rule_intersection (int x, int y, int c, int r)
1336 /* Bounds of intersection. */
1339 int x1 = x + t->wrv[c];
1340 int y1 = y + t->hrh[r];
1342 /* Lines on each side of intersection. */
1343 int top = r > 0 ? get_vrule (c, r - 1) : TAL_0;
1344 int left = c > 0 ? get_hrule (c - 1, r) : TAL_0;
1345 int bottom = r < t->nr ? get_vrule (c, r) : TAL_0;
1346 int right = c < t->nc ? get_hrule (c, r) : TAL_0;
1348 /* Output style for each line. */
1349 enum outp_line_style o_top = rule_to_draw_type (top);
1350 enum outp_line_style o_left = rule_to_draw_type (left);
1351 enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1352 enum outp_line_style o_right = rule_to_draw_type (right);
1354 if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1355 || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1356 d->class->line (d, x0, y0, x1, y1, o_top, o_left, o_bottom, o_right);
1359 /* Returns the width of columns C1...C2 exclusive,
1360 including interior but not exterior rules. */
1362 strip_width (int c1, int c2)
1367 for (c = c1; c < c2; c++)
1368 width += t->w[c] + t->wrv[c + 1];
1370 width -= t->wrv[c2];
1374 /* Returns the height of rows R1...R2 exclusive,
1375 including interior but not exterior rules. */
1377 strip_height (int r1, int r2)
1382 for (r = r1; r < r2; r++)
1383 height += t->h[r] + t->hrh[r + 1];
1385 height -= t->hrh[r2];
1389 /* Renders the cell at the given column and row at (X,Y) on the
1390 page. Also renders joined cells that extend as far to the
1391 right as C1 and as far down as R1. */
1393 render_cell (int x, int y, int c, int r, int c1, int r1)
1395 const int index = c + (r * t->cf);
1396 unsigned char type = t->ct[index];
1397 struct fixed_string *content = &t->cc[index];
1399 if (!(type & TAB_JOIN))
1401 if (!(type & TAB_EMPTY))
1403 struct outp_text text;
1404 text.font = options_to_font (type);
1405 text.justification = translate_justification (type);
1406 text.string = *content;
1411 d->class->text_draw (d, &text);
1416 struct tab_joined_cell *j
1417 = (struct tab_joined_cell *) ls_c_str (content);
1419 if (j->hit != tab_hit)
1423 if (j->x1 == c && j->y1 == r)
1425 struct outp_text text;
1426 text.font = options_to_font (type);
1427 text.justification = translate_justification (type);
1428 text.string = j->contents;
1431 text.h = strip_width (j->x1, MIN (j->x2, c1));
1432 text.v = strip_height (j->y1, MIN (j->y2, r1));
1433 d->class->text_draw (d, &text);
1439 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1440 on row R, at (X,Y). Returns X position after rendering.
1441 Also renders joined cells that extend beyond that strip,
1442 cropping them to lie within rendering region (C0,R0)-(C1,R1).
1443 C0 and C1 count vertical rules as columns.
1444 R counts horizontal rules as rows, but R0 and R1 do not. */
1446 render_strip (int x, int y, int r, int c0, int c1, int r0 UNUSED, int r1)
1450 for (c = c0; c < c1; c++)
1454 render_cell (x, y, c / 2, r / 2, c1 / 2, r1);
1456 render_horz_rule (x, y, c / 2, r / 2);
1462 render_vert_rule (x, y, c / 2, r / 2);
1464 render_rule_intersection (x, y, c / 2, r / 2);
1471 /* Sets COMMAND_NAME as the name of the current command,
1472 for embedding in output. */
1474 tab_set_command_name (const char *command_name_)
1476 free (command_name);
1477 command_name = command_name_ ? xstrdup (command_name_) : NULL;