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 fixed_string *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 and sets
376 the resultant string into S in TABLE's pool. */
378 text_format (struct tab_table *table, int opt, const char *text, va_list args,
379 struct fixed_string *s)
383 assert (table != NULL && text != NULL && s != NULL);
385 if (opt & TAT_PRINTF)
386 text = tmp = xvasprintf (text, args);
388 ls_create_buffer (s, text, strlen (text));
389 pool_register (table->container, free, s->string);
394 /* Set the title of table T to TITLE, which is formatted as if
395 passed to printf(). */
397 tab_title (struct tab_table *t, const char *title, ...)
401 assert (t != NULL && title != NULL);
402 va_start (args, title);
403 text_format (t, TAT_PRINTF, title, args, &t->title);
407 /* Set DIM_FUNC as the dimension function for table T. */
409 tab_dim (struct tab_table *t, tab_dim_func *dim_func)
411 assert (t != NULL && t->dim == NULL);
415 /* Returns the natural width of column C in table T for driver D, that
416 is, the smallest width necessary to display all its cells without
417 wrapping. The width will be no larger than the page width minus
418 left and right rule widths. */
420 tab_natural_width (struct tab_table *t, struct outp_driver *d, int c)
424 assert (t != NULL && c >= 0 && c < t->nc);
428 for (width = r = 0; r < t->nr; r++)
430 struct outp_text text;
431 unsigned char opt = t->ct[c + r * t->cf];
434 if (opt & (TAB_JOIN | TAB_EMPTY))
437 text.string = t->cc[c + r * t->cf];
438 assert (!ls_null_p (&text.string));
439 text.justification = OUTP_LEFT;
440 text.font = options_to_font (opt);
441 text.h = text.v = INT_MAX;
443 d->class->text_metrics (d, &text, &w, NULL);
451 width = d->prop_em_width * 8;
453 printf ("warning: table column %d contains no data.\n", c);
458 const int clamp = d->width - t->wrv[0] - t->wrv[t->nc];
467 /* Returns the natural height of row R in table T for driver D, that
468 is, the minimum height necessary to display the information in the
469 cell at the widths set for each column. */
471 tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
475 assert (t != NULL && r >= 0 && r < t->nr);
480 for (height = d->font_height, c = 0; c < t->nc; c++)
482 struct outp_text text;
483 unsigned char opt = t->ct[c + r * t->cf];
486 assert (t->w[c] != NOT_INT);
487 if (opt & (TAB_JOIN | TAB_EMPTY))
490 text.string = t->cc[c + r * t->cf];
491 assert (!ls_null_p (&text.string));
492 text.justification = OUTP_LEFT;
493 text.font = options_to_font (opt);
496 d->class->text_metrics (d, &text, NULL, &h);
506 /* Callback function to set all columns and rows to their natural
507 dimensions. Not really meant to be called directly. */
509 tab_natural_dimensions (struct tab_table *t, struct outp_driver *d)
515 for (i = 0; i < t->nc; i++)
516 t->w[i] = tab_natural_width (t, d, i);
518 for (i = 0; i < t->nr; i++)
519 t->h[i] = tab_natural_height (t, d, i);
525 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
526 from V, displayed with format spec F. */
528 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
529 const union value *v, const struct fmt_spec *f)
533 assert (table != NULL && v != NULL && f != NULL);
535 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
536 || c + table->col_ofs >= table->nc
537 || r + table->row_ofs >= table->nr)
539 printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
541 c, table->col_ofs, c + table->col_ofs,
542 r, table->row_ofs, r + table->row_ofs,
543 table->nc, table->nr);
548 contents = pool_alloc (table->container, f->w);
549 ls_init (&table->cc[c + r * table->cf], contents, f->w);
550 table->ct[c + r * table->cf] = opt;
552 data_out (contents, f, v);
555 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
556 with NDEC decimal places. */
558 tab_float (struct tab_table *table, int c, int r, unsigned char opt,
559 double val, int w, int d)
565 union value double_value;
567 assert (table != NULL && w <= 40);
570 assert (c < table->nc);
572 assert (r < table->nr);
574 f = make_output_format (FMT_F, w, d);
577 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
578 || c + table->col_ofs >= table->nc
579 || r + table->row_ofs >= table->nr)
581 printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
583 c, table->col_ofs, c + table->col_ofs,
584 r, table->row_ofs, r + table->row_ofs,
585 table->nc, table->nr);
590 double_value.f = val;
591 data_out (buf, &f, &double_value);
594 while (isspace ((unsigned char) *cp) && cp < &buf[w])
596 f.w = w - (cp - buf);
598 contents = pool_alloc (table->container, f.w);
599 ls_init (&table->cc[c + r * table->cf], contents, f.w);
600 table->ct[c + r * table->cf] = opt;
601 memcpy (contents, cp, f.w);
604 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
607 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
611 assert (table != NULL && text != NULL);
615 assert (c < table->nc);
616 assert (r < table->nr);
620 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
621 || c + table->col_ofs >= table->nc
622 || r + table->row_ofs >= table->nr)
624 printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
626 c, table->col_ofs, c + table->col_ofs,
627 r, table->row_ofs, r + table->row_ofs,
628 table->nc, table->nr);
633 va_start (args, text);
634 text_format (table, opt, text, args, &table->cc[c + r * table->cf]);
635 table->ct[c + r * table->cf] = opt;
639 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
640 options OPT to have text value TEXT. */
642 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
643 unsigned opt, const char *text, ...)
645 struct tab_joined_cell *j;
647 assert (table != NULL && text != NULL);
649 assert (x1 + table->col_ofs >= 0);
650 assert (y1 + table->row_ofs >= 0);
653 assert (y2 + table->row_ofs < table->nr);
654 assert (x2 + table->col_ofs < table->nc);
657 if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
658 || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
659 || x2 < x1 || x2 + table->col_ofs >= table->nc
660 || y2 < y2 || y2 + table->row_ofs >= table->nr)
662 printf ("tab_joint_text(): bad cell "
663 "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
664 x1, table->col_ofs, x1 + table->col_ofs,
665 y1, table->row_ofs, y1 + table->row_ofs,
666 x2, table->col_ofs, x2 + table->col_ofs,
667 y2, table->row_ofs, y2 + table->row_ofs,
668 table->nc, table->nr);
673 tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
675 j = pool_alloc (table->container, sizeof *j);
677 j->x1 = x1 + table->col_ofs;
678 j->y1 = y1 + table->row_ofs;
679 j->x2 = ++x2 + table->col_ofs;
680 j->y2 = ++y2 + table->row_ofs;
685 va_start (args, text);
686 text_format (table, opt, text, args, &j->contents);
693 struct fixed_string *cc = &table->cc[x1 + y1 * table->cf];
694 unsigned char *ct = &table->ct[x1 + y1 * table->cf];
695 const int ofs = table->cf - (x2 - x1);
699 for (y = y1; y < y2; y++)
703 for (x = x1; x < x2; x++)
705 ls_init (cc++, (char *) j, 0);
715 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
717 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
718 struct fixed_string *string)
720 assert (table != NULL && string != NULL);
723 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
724 || c + table->col_ofs >= table->nc
725 || r + table->row_ofs >= table->nr)
727 printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
729 c, table->col_ofs, c + table->col_ofs,
730 r, table->row_ofs, r + table->row_ofs,
731 table->nc, table->nr);
736 table->cc[c + r * table->cf] = *string;
737 table->ct[c + r * table->cf] = opt;
742 /* Sets the widths of all the columns and heights of all the rows in
743 table T for driver D. */
745 nowrap_dim (struct tab_table *t, struct outp_driver *d)
747 t->w[0] = tab_natural_width (t, d, 0);
748 t->h[0] = d->font_height;
751 /* Sets the widths of all the columns and heights of all the rows in
752 table T for driver D. */
754 wrap_dim (struct tab_table *t, struct outp_driver *d)
756 t->w[0] = tab_natural_width (t, d, 0);
757 t->h[0] = tab_natural_height (t, d, 0);
760 /* Outputs text BUF as a table with a single cell having cell options
761 OPTIONS, which is a combination of the TAB_* and TAT_*
764 tab_output_text (int options, const char *buf, ...)
766 struct tab_table *t = tab_create (1, 1, 0);
767 char *tmp_buf = NULL;
769 if (options & TAT_PRINTF)
773 va_start (args, buf);
774 buf = tmp_buf = xvasprintf (buf, args);
778 tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
779 tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
780 tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim);
786 /* Set table flags to FLAGS. */
788 tab_flags (struct tab_table *t, unsigned flags)
794 /* Easy, type-safe way to submit a tab table to som. */
796 tab_submit (struct tab_table *t)
801 s.class = &tab_table_class;
810 /* Set table row and column offsets for all functions that affect
813 tab_offset (struct tab_table *t, int col, int row)
819 if (row < -1 || row >= t->nr)
821 printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
824 if (col < -1 || col >= t->nc)
826 printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
832 diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
834 diff += (col - t->col_ofs), t->col_ofs = col;
840 /* Increment the row offset by one. If the table is too small,
841 increase its size. */
843 tab_next_row (struct tab_table *t)
848 if (++t->row_ofs >= t->nr)
849 tab_realloc (t, -1, t->nr * 4 / 3);
852 static struct tab_table *t;
853 static struct outp_driver *d;
856 /* Set the current table to TABLE. */
858 tabi_table (struct som_entity *table)
860 assert (table != NULL);
861 assert (table->type == SOM_TABLE);
864 tab_offset (t, 0, 0);
866 assert (t->w == NULL && t->h == NULL);
867 t->w = pool_nalloc (t->container, t->nc, sizeof *t->w);
868 t->h = pool_nalloc (t->container, t->nr, sizeof *t->h);
871 /* Returns the line style to use for spacing purposes for a rule
872 of the given TYPE. */
873 static enum outp_line_style
874 rule_to_spacing_type (unsigned char type)
882 return OUTP_L_SINGLE;
884 return OUTP_L_DOUBLE;
890 /* Set the current output device to DRIVER. */
892 tabi_driver (struct outp_driver *driver)
897 assert (driver != NULL);
900 /* Figure out sizes of rules. */
901 for (r = 0; r <= t->nr; r++)
904 for (c = 0; c < t->nc; c++)
906 unsigned char rh = t->rh[c + r * t->cf];
907 int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
914 for (c = 0; c <= t->nc; c++)
917 for (r = 0; r < t->nr; r++)
919 unsigned char *rv = &t->rv[c + r * (t->cf + 1)];
921 if (*rv == UCHAR_MAX)
922 *rv = c != 0 && c != t->nc ? TAL_GAP : TAL_0;
923 w = driver->vert_line_width[rule_to_spacing_type (*rv)];
931 for (i = 0; i < t->nr; i++)
933 for (i = 0; i < t->nc; i++)
937 assert (t->dim != NULL);
944 for (i = 0; i < t->nr; i++)
948 printf ("Table row %d height not initialized.\n", i);
951 assert (t->h[i] > 0);
954 for (i = 0; i < t->nc; i++)
958 printf ("Table column %d width not initialized.\n", i);
961 assert (t->w[i] > 0);
966 /* Add up header sizes. */
967 for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
968 t->wl += t->w[i] + t->wrv[i + 1];
969 for (i = 0, t->ht = t->hrh[0]; i < t->t; i++)
970 t->ht += t->h[i] + t->hrh[i + 1];
971 for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++)
972 t->wr += t->w[i] + t->wrv[i + 1];
973 for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
974 t->hb += t->h[i] + t->hrh[i + 1];
977 if (!(t->flags & SOMF_NO_TITLE))
978 t->ht += d->font_height;
981 /* Return the number of columns and rows in the table into N_COLUMNS
982 and N_ROWS, respectively. */
984 tabi_count (int *n_columns, int *n_rows)
986 assert (n_columns != NULL && n_rows != NULL);
991 static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual);
993 /* Return the horizontal and vertical size of the entire table,
994 including headers, for the current output device, into HORIZ and
997 tabi_area (int *horiz, int *vert)
999 assert (horiz != NULL && vert != NULL);
1004 for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
1005 c < t->nc - t->r; c++)
1006 w += t->w[c] + t->wrv[c];
1012 for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
1013 r < t->nr - t->b; r++)
1014 h += t->h[r] + t->hrh[r];
1019 /* Return the column style for this table into STYLE. */
1021 tabi_columns (int *style)
1023 assert (style != NULL);
1024 *style = t->col_style;
1027 /* Return the number of header rows/columns on the left, right, top,
1028 and bottom sides into HL, HR, HT, and HB, respectively. */
1030 tabi_headers (int *hl, int *hr, int *ht, int *hb)
1032 assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL);
1039 /* Determines the number of rows or columns (including appropriate
1040 headers), depending on CUMTYPE, that will fit into the space
1041 specified. Takes rows/columns starting at index START and attempts
1042 to fill up available space MAX. Returns in END the index of the
1043 last row/column plus one; returns in ACTUAL the actual amount of
1044 space the selected rows/columns (including appropriate headers)
1047 tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
1054 assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1055 if (cumtype == SOM_ROWS)
1057 assert (start >= 0 && start < t->nr);
1060 r = &t->hrh[start + 1];
1061 total = t->ht + t->hb;
1065 assert (start >= 0 && start < t->nc);
1068 r = &t->wrv[start + 1];
1069 total = t->wl + t->wr;
1085 for (x = start + 1; x < n; x++)
1087 int amt = *d++ + *r++;
1105 /* Return flags set for the current table into FLAGS. */
1107 tabi_flags (unsigned *flags)
1109 assert (flags != NULL);
1113 /* Returns true if the table will fit in the given page WIDTH,
1116 tabi_fits_width (int width)
1120 for (i = t->l; i < t->nc - t->r; i++)
1121 if (t->wl + t->wr + t->w[i] > width)
1127 /* Returns true if the table will fit in the given page LENGTH,
1130 tabi_fits_length (int length)
1134 for (i = t->t; i < t->nr - t->b; i++)
1135 if (t->ht + t->hb + t->h[i] > length)
1141 /* Sets the number of header rows/columns on the left, right, top,
1142 and bottom sides to HL, HR, HT, and HB, respectively. */
1144 tabi_set_headers (int hl, int hr, int ht, int hb)
1152 /* Render title for current table, with major index X and minor index
1153 Y. Y may be zero, or X and Y may be zero, but X should be nonzero
1156 tabi_title (int x, int y)
1161 if (t->flags & SOMF_NO_TITLE)
1164 cp = spprintf (buf, "%d.%d", table_num, subtable_num);
1166 cp = spprintf (cp, "(%d:%d)", x, y);
1168 cp = spprintf (cp, "(%d)", x);
1169 if (command_name != NULL)
1170 cp = spprintf (cp, " %s", command_name);
1171 cp = stpcpy (cp, ". ");
1172 if (!ls_empty_p (&t->title))
1174 memcpy (cp, ls_c_str (&t->title), ls_length (&t->title));
1175 cp += ls_length (&t->title);
1180 struct outp_text text;
1182 text.font = OUTP_PROPORTIONAL;
1183 text.justification = OUTP_LEFT;
1184 ls_init (&text.string, buf, cp - buf);
1186 text.v = d->font_height;
1189 d->class->text_draw (d, &text);
1193 static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
1195 /* Renders columns C0...C1, plus headers, of rows R0...R1,
1196 at the given vertical position Y.
1197 C0 and C1 count vertical rules as columns,
1198 but R0 and R1 do not count horizontal rules as rows.
1199 Returns the vertical position after rendering. */
1201 render_rows (int y, int c0, int c1, int r0, int r1)
1204 for (r = r0; r < r1; r++)
1207 x = render_strip (x, y, r, 0, t->l * 2 + 1, r0, r1);
1208 x = render_strip (x, y, r, c0 * 2 + 1, c1 * 2, r0, r1);
1209 x = render_strip (x, y, r, (t->nc - t->r) * 2, t->nc * 2 + 1, r0, r1);
1210 y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2];
1215 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1216 current position on the current output device. */
1218 tabi_render (int c0, int r0, int c1, int r1)
1225 if (!(t->flags & SOMF_NO_TITLE))
1226 y += d->font_height;
1228 y = render_rows (y, c0, c1, 0, t->t * 2 + 1);
1229 y = render_rows (y, c0, c1, r0 * 2 + 1, r1 * 2);
1230 y = render_rows (y, c0, c1, (t->nr - t->b) * 2, t->nr * 2 + 1);
1233 struct som_table_class tab_table_class =
1259 static enum outp_justification
1260 translate_justification (unsigned int opt)
1262 switch (opt & TAB_ALIGN_MASK)
1275 /* Returns the line style to use for drawing a rule of the given
1277 static enum outp_line_style
1278 rule_to_draw_type (unsigned char type)
1286 return OUTP_L_SINGLE;
1288 return OUTP_L_DOUBLE;
1294 /* Returns the horizontal rule at the given column and row. */
1296 get_hrule (int c, int r)
1298 return t->rh[c + r * t->cf];
1301 /* Returns the vertical rule at the given column and row. */
1303 get_vrule (int c, int r)
1305 return t->rv[c + r * (t->cf + 1)];
1308 /* Renders the horizontal rule at the given column and row
1309 at (X,Y) on the page. */
1311 render_horz_rule (int x, int y, int c, int r)
1313 enum outp_line_style style = rule_to_draw_type (get_hrule (c, r));
1314 if (style != OUTP_L_NONE)
1315 d->class->line (d, x, y, x + t->w[c], y + t->hrh[r],
1316 OUTP_L_NONE, style, OUTP_L_NONE, style);
1319 /* Renders the vertical rule at the given column and row
1320 at (X,Y) on the page. */
1322 render_vert_rule (int x, int y, int c, int r)
1324 enum outp_line_style style = rule_to_draw_type (get_vrule (c, r));
1325 if (style != OUTP_L_NONE)
1326 d->class->line (d, x, y, x + t->wrv[c], y + t->h[r],
1327 style, OUTP_L_NONE, style, OUTP_L_NONE);
1330 /* Renders the rule intersection at the given column and row
1331 at (X,Y) on the page. */
1333 render_rule_intersection (int x, int y, int c, int r)
1335 /* Bounds of intersection. */
1338 int x1 = x + t->wrv[c];
1339 int y1 = y + t->hrh[r];
1341 /* Lines on each side of intersection. */
1342 int top = r > 0 ? get_vrule (c, r - 1) : TAL_0;
1343 int left = c > 0 ? get_hrule (c - 1, r) : TAL_0;
1344 int bottom = r < t->nr ? get_vrule (c, r) : TAL_0;
1345 int right = c < t->nc ? get_hrule (c, r) : TAL_0;
1347 /* Output style for each line. */
1348 enum outp_line_style o_top = rule_to_draw_type (top);
1349 enum outp_line_style o_left = rule_to_draw_type (left);
1350 enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1351 enum outp_line_style o_right = rule_to_draw_type (right);
1353 if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1354 || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1355 d->class->line (d, x0, y0, x1, y1, o_top, o_left, o_bottom, o_right);
1358 /* Returns the width of columns C1...C2 exclusive,
1359 including interior but not exterior rules. */
1361 strip_width (int c1, int c2)
1366 for (c = c1; c < c2; c++)
1367 width += t->w[c] + t->wrv[c + 1];
1369 width -= t->wrv[c2];
1373 /* Returns the height of rows R1...R2 exclusive,
1374 including interior but not exterior rules. */
1376 strip_height (int r1, int r2)
1381 for (r = r1; r < r2; r++)
1382 height += t->h[r] + t->hrh[r + 1];
1384 height -= t->hrh[r2];
1388 /* Renders the cell at the given column and row at (X,Y) on the
1389 page. Also renders joined cells that extend as far to the
1390 right as C1 and as far down as R1. */
1392 render_cell (int x, int y, int c, int r, int c1, int r1)
1394 const int index = c + (r * t->cf);
1395 unsigned char type = t->ct[index];
1396 struct fixed_string *content = &t->cc[index];
1398 if (!(type & TAB_JOIN))
1400 if (!(type & TAB_EMPTY))
1402 struct outp_text text;
1403 text.font = options_to_font (type);
1404 text.justification = translate_justification (type);
1405 text.string = *content;
1410 d->class->text_draw (d, &text);
1415 struct tab_joined_cell *j
1416 = (struct tab_joined_cell *) ls_c_str (content);
1418 if (j->hit != tab_hit)
1422 if (j->x1 == c && j->y1 == r)
1424 struct outp_text text;
1425 text.font = options_to_font (type);
1426 text.justification = translate_justification (type);
1427 text.string = j->contents;
1430 text.h = strip_width (j->x1, MIN (j->x2, c1));
1431 text.v = strip_height (j->y1, MIN (j->y2, r1));
1432 d->class->text_draw (d, &text);
1438 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1439 on row R, at (X,Y). Returns X position after rendering.
1440 Also renders joined cells that extend beyond that strip,
1441 cropping them to lie within rendering region (C0,R0)-(C1,R1).
1442 C0 and C1 count vertical rules as columns.
1443 R counts horizontal rules as rows, but R0 and R1 do not. */
1445 render_strip (int x, int y, int r, int c0, int c1, int r0 UNUSED, int r1)
1449 for (c = c0; c < c1; c++)
1453 render_cell (x, y, c / 2, r / 2, c1 / 2, r1);
1455 render_horz_rule (x, y, c / 2, r / 2);
1461 render_vert_rule (x, y, c / 2, r / 2);
1463 render_rule_intersection (x, y, c / 2, r / 2);
1470 /* Sets COMMAND_NAME as the name of the current command,
1471 for embedding in output. */
1473 tab_set_command_name (const char *command_name_)
1475 free (command_name);
1476 command_name = command_name_ ? xstrdup (command_name_) : NULL;