1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
29 #include <data/data-out.h>
30 #include <data/format.h>
31 #include <data/value.h>
32 #include <libpspp/alloc.h>
33 #include <libpspp/assertion.h>
34 #include <libpspp/compiler.h>
35 #include <libpspp/magic.h>
36 #include <libpspp/misc.h>
37 #include <libpspp/pool.h>
42 #define _(msgid) gettext (msgid)
44 const struct som_table_class tab_table_class;
45 static char *command_name;
47 /* Returns the font to use for a cell with the given OPTIONS. */
49 options_to_font (unsigned options)
51 return (options & TAB_FIX ? OUTP_FIXED
52 : options & TAB_EMPH ? OUTP_EMPHASIS
56 /* Creates a table with NC columns and NR rows. */
58 tab_create (int nc, int nr, int reallocable UNUSED)
62 t = pool_create_container (struct tab_table, container);
63 t->col_style = TAB_COL_NONE;
69 t->l = t->r = t->t = t->b = 0;
71 t->cc = pool_nmalloc (t->container, nr * nc, sizeof *t->cc);
72 t->ct = pool_malloc (t->container, nr * nc);
73 memset (t->ct, TAB_EMPTY, nc * nr);
75 t->rh = pool_nmalloc (t->container, nc, nr + 1);
76 memset (t->rh, 0, nc * (nr + 1));
78 t->rv = pool_nmalloc (t->container, nr, nc + 1);
79 memset (t->rv, UCHAR_MAX, nr * (nc + 1));
83 t->col_ofs = t->row_ofs = 0;
88 /* Destroys table T. */
90 tab_destroy (struct tab_table *t)
94 pool_destroy (t->container);
97 /* Sets the width and height of a table, in columns and rows,
98 respectively. Use only to reduce the size of a table, since it
99 does not change the amount of allocated memory. */
101 tab_resize (struct tab_table *t, int nc, int nr)
106 assert (nc + t->col_ofs <= t->cf);
107 t->nc = nc + t->col_ofs;
111 assert (nr + t->row_ofs <= t->nr);
112 t->nr = nr + t->row_ofs;
116 /* Changes either or both dimensions of a table. Consider using the
117 above routine instead if it won't waste a lot of space.
119 Changing the number of columns in a table is particularly expensive
120 in space and time. Avoid doing such. FIXME: In fact, transferring
121 of rules isn't even implemented yet. */
123 tab_realloc (struct tab_table *t, int nc, int nr)
131 tab_offset (t, 0, 0);
138 assert (nc == t->nc);
142 int mr1 = MIN (nr, t->nr);
143 int mc1 = MIN (nc, t->nc);
145 struct substring *new_cc;
146 unsigned char *new_ct;
149 new_cc = pool_nmalloc (t->container, nr * nc, sizeof *new_cc);
150 new_ct = pool_malloc (t->container, nr * nc);
151 for (r = 0; r < mr1; r++)
153 memcpy (&new_cc[r * nc], &t->cc[r * t->nc], mc1 * sizeof *t->cc);
154 memcpy (&new_ct[r * nc], &t->ct[r * t->nc], mc1);
155 memset (&new_ct[r * nc + t->nc], TAB_EMPTY, nc - t->nc);
157 pool_free (t->container, t->cc);
158 pool_free (t->container, t->ct);
163 else if (nr != t->nr)
165 t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc);
166 t->ct = pool_realloc (t->container, t->ct, nr * nc);
168 t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
169 t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
173 memset (&t->rh[nc * (t->nr + 1)], TAL_0, (nr - t->nr) * nc);
174 memset (&t->rv[(nc + 1) * t->nr], UCHAR_MAX,
175 (nr - t->nr) * (nc + 1));
179 memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr));
185 tab_offset (t, co, ro);
188 /* Sets the number of header rows on each side of TABLE to L on the
189 left, R on the right, T on the top, B on the bottom. Header rows
190 are repeated when a table is broken across multiple columns or
193 tab_headers (struct tab_table *table, int l, int r, int t, int b)
195 assert (table != NULL);
196 assert (l < table->nc);
197 assert (r < table->nc);
198 assert (t < table->nr);
199 assert (b < table->nr);
208 /* Set up table T so that, when it is an appropriate size, it will be
209 displayed across the page in columns.
211 STYLE is a TAB_COL_* constant. GROUP is the number of rows to take
214 tab_columns (struct tab_table *t, int style, int group)
217 t->col_style = style;
218 t->col_group = group;
223 /* Draws a vertical line to the left of cells at horizontal position X
224 from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
226 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
231 if (x + t->col_ofs < 0 || x + t->col_ofs > t->nc
232 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
233 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
235 printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
236 "table size (%d,%d)\n"),
237 x, t->col_ofs, x + t->col_ofs,
238 y1, t->row_ofs, y1 + t->row_ofs,
239 y2, t->row_ofs, y2 + t->row_ofs,
253 assert (y2 <= t->nr);
258 for (y = y1; y <= y2; y++)
259 t->rv[x + (t->cf + 1) * y] = style;
263 /* Draws a horizontal line above cells at vertical position Y from X1
264 to X2 inclusive in style STYLE, if style is not -1. */
266 tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
283 for (x = x1; x <= x2; x++)
284 t->rh[x + t->cf * y] = style;
288 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
289 lines of style F_H and vertical lines of style F_V. Fills the
290 interior of the box with horizontal lines of style I_H and vertical
291 lines of style I_V. Any of the line styles may be -1 to avoid
292 drawing those lines. This is distinct from 0, which draws a null
295 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
296 int x1, int y1, int x2, int y2)
301 if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc
302 || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= t->nc
303 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
304 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
306 printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
307 "in table size (%d,%d)\n"),
308 x1, t->col_ofs, x1 + t->col_ofs,
309 y1, t->row_ofs, y1 + t->row_ofs,
310 x2, t->col_ofs, x2 + t->col_ofs,
311 y2, t->row_ofs, y2 + t->row_ofs,
332 for (x = x1; x <= x2; x++)
334 t->rh[x + t->cf * y1] = f_h;
335 t->rh[x + t->cf * (y2 + 1)] = f_h;
341 for (y = y1; y <= y2; y++)
343 t->rv[x1 + (t->cf + 1) * y] = f_v;
344 t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
352 for (y = y1 + 1; y <= y2; y++)
356 for (x = x1; x <= x2; x++)
357 t->rh[x + t->cf * y] = i_h;
364 for (x = x1 + 1; x <= x2; x++)
368 for (y = y1; y <= y2; y++)
369 t->rv[x + (t->cf + 1) * y] = i_v;
374 /* Formats text TEXT and arguments ARGS as indicated in OPT in
375 TABLE's pool and returns the resultant string. */
376 static struct substring
377 text_format (struct tab_table *table, int opt, const char *text, va_list args)
379 assert (table != NULL && text != NULL);
381 return ss_cstr (opt & TAT_PRINTF
382 ? pool_vasprintf (table->container, text, args)
383 : pool_strdup (table->container, text));
386 /* Set the title of table T to TITLE, which is formatted as if
387 passed to printf(). */
389 tab_title (struct tab_table *t, const char *title, ...)
393 assert (t != NULL && title != NULL);
394 va_start (args, title);
395 t->title = xvasprintf (title, args);
399 /* Set DIM_FUNC as the dimension function for table T. */
401 tab_dim (struct tab_table *t, tab_dim_func *dim_func)
403 assert (t != NULL && t->dim == NULL);
407 /* Returns the natural width of column C in table T for driver D, that
408 is, the smallest width necessary to display all its cells without
409 wrapping. The width will be no larger than the page width minus
410 left and right rule widths. */
412 tab_natural_width (struct tab_table *t, struct outp_driver *d, int c)
416 assert (t != NULL && c >= 0 && c < t->nc);
420 for (width = r = 0; r < t->nr; r++)
422 struct outp_text text;
423 unsigned char opt = t->ct[c + r * t->cf];
426 if (opt & (TAB_JOIN | TAB_EMPTY))
429 text.string = t->cc[c + r * t->cf];
430 text.justification = OUTP_LEFT;
431 text.font = options_to_font (opt);
432 text.h = text.v = INT_MAX;
434 d->class->text_metrics (d, &text, &w, NULL);
442 /* FIXME: This is an ugly kluge to compensate for the fact
443 that we don't let joined cells contribute to column
445 width = d->prop_em_width * 8;
449 const int clamp = d->width - t->wrv[0] - t->wrv[t->nc];
458 /* Returns the natural height of row R in table T for driver D, that
459 is, the minimum height necessary to display the information in the
460 cell at the widths set for each column. */
462 tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
466 assert (t != NULL && r >= 0 && r < t->nr);
471 for (height = d->font_height, c = 0; c < t->nc; c++)
473 struct outp_text text;
474 unsigned char opt = t->ct[c + r * t->cf];
477 assert (t->w[c] != NOT_INT);
478 if (opt & (TAB_JOIN | TAB_EMPTY))
481 text.string = t->cc[c + r * t->cf];
482 text.justification = OUTP_LEFT;
483 text.font = options_to_font (opt);
486 d->class->text_metrics (d, &text, NULL, &h);
496 /* Callback function to set all columns and rows to their natural
497 dimensions. Not really meant to be called directly. */
499 tab_natural_dimensions (struct tab_table *t, struct outp_driver *d)
505 for (i = 0; i < t->nc; i++)
506 t->w[i] = tab_natural_width (t, d, i);
508 for (i = 0; i < t->nr; i++)
509 t->h[i] = tab_natural_height (t, d, i);
515 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
516 from V, displayed with format spec F. */
518 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
519 const union value *v, const struct fmt_spec *f)
523 assert (table != NULL && v != NULL && f != NULL);
525 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
526 || c + table->col_ofs >= table->nc
527 || r + table->row_ofs >= table->nr)
529 printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
531 c, table->col_ofs, c + table->col_ofs,
532 r, table->row_ofs, r + table->row_ofs,
533 table->nc, table->nr);
538 contents = pool_alloc (table->container, f->w);
539 table->cc[c + r * table->cf] = ss_buffer (contents, f->w);
540 table->ct[c + r * table->cf] = opt;
542 data_out (v, f, contents);
545 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
546 with NDEC decimal places. */
548 tab_float (struct tab_table *table, int c, int r, unsigned char opt,
549 double val, int w, int d)
555 union value double_value;
557 assert (table != NULL && w <= 40);
560 assert (c < table->nc);
562 assert (r < table->nr);
564 f = fmt_for_output (FMT_F, w, d);
567 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
568 || c + table->col_ofs >= table->nc
569 || r + table->row_ofs >= table->nr)
571 printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
573 c, table->col_ofs, c + table->col_ofs,
574 r, table->row_ofs, r + table->row_ofs,
575 table->nc, table->nr);
580 double_value.f = val;
581 data_out (&double_value, &f, buf);
584 while (isspace ((unsigned char) *cp) && cp < &buf[w])
586 f.w = w - (cp - buf);
588 contents = pool_alloc (table->container, f.w);
589 table->cc[c + r * table->cf] = ss_buffer (contents, f.w);
590 table->ct[c + r * table->cf] = opt;
591 memcpy (contents, cp, f.w);
594 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
597 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
601 assert (table != NULL && text != NULL);
605 assert (c < table->nc);
606 assert (r < table->nr);
610 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
611 || c + table->col_ofs >= table->nc
612 || r + table->row_ofs >= table->nr)
614 printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
616 c, table->col_ofs, c + table->col_ofs,
617 r, table->row_ofs, r + table->row_ofs,
618 table->nc, table->nr);
623 va_start (args, text);
624 table->cc[c + r * table->cf] = text_format (table, opt, text, args);
625 table->ct[c + r * table->cf] = opt;
629 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
630 options OPT to have text value TEXT. */
632 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
633 unsigned opt, const char *text, ...)
635 struct tab_joined_cell *j;
637 assert (table != NULL && text != NULL);
639 assert (x1 + table->col_ofs >= 0);
640 assert (y1 + table->row_ofs >= 0);
643 assert (y2 + table->row_ofs < table->nr);
644 assert (x2 + table->col_ofs < table->nc);
647 if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
648 || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
649 || x2 < x1 || x2 + table->col_ofs >= table->nc
650 || y2 < y2 || y2 + table->row_ofs >= table->nr)
652 printf ("tab_joint_text(): bad cell "
653 "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
654 x1, table->col_ofs, x1 + table->col_ofs,
655 y1, table->row_ofs, y1 + table->row_ofs,
656 x2, table->col_ofs, x2 + table->col_ofs,
657 y2, table->row_ofs, y2 + table->row_ofs,
658 table->nc, table->nr);
663 tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
665 j = pool_alloc (table->container, sizeof *j);
667 j->x1 = x1 + table->col_ofs;
668 j->y1 = y1 + table->row_ofs;
669 j->x2 = ++x2 + table->col_ofs;
670 j->y2 = ++y2 + table->row_ofs;
675 va_start (args, text);
676 j->contents = text_format (table, opt, text, args);
683 struct substring *cc = &table->cc[x1 + y1 * table->cf];
684 unsigned char *ct = &table->ct[x1 + y1 * table->cf];
685 const int ofs = table->cf - (x2 - x1);
689 for (y = y1; y < y2; y++)
693 for (x = x1; x < x2; x++)
695 *cc++ = ss_buffer ((char *) j, 0);
705 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
707 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
708 struct substring *string)
710 assert (table != NULL && string != NULL);
713 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
714 || c + table->col_ofs >= table->nc
715 || r + table->row_ofs >= table->nr)
717 printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
719 c, table->col_ofs, c + table->col_ofs,
720 r, table->row_ofs, r + table->row_ofs,
721 table->nc, table->nr);
726 table->cc[c + r * table->cf] = *string;
727 table->ct[c + r * table->cf] = opt;
732 /* Sets the widths of all the columns and heights of all the rows in
733 table T for driver D. */
735 nowrap_dim (struct tab_table *t, struct outp_driver *d)
737 t->w[0] = tab_natural_width (t, d, 0);
738 t->h[0] = d->font_height;
741 /* Sets the widths of all the columns and heights of all the rows in
742 table T for driver D. */
744 wrap_dim (struct tab_table *t, struct outp_driver *d)
746 t->w[0] = tab_natural_width (t, d, 0);
747 t->h[0] = tab_natural_height (t, d, 0);
750 /* Outputs text BUF as a table with a single cell having cell options
751 OPTIONS, which is a combination of the TAB_* and TAT_*
754 tab_output_text (int options, const char *buf, ...)
756 struct tab_table *t = tab_create (1, 1, 0);
757 char *tmp_buf = NULL;
759 if (options & TAT_PRINTF)
763 va_start (args, buf);
764 buf = tmp_buf = xvasprintf (buf, args);
768 tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
769 tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
770 tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim);
776 /* Set table flags to FLAGS. */
778 tab_flags (struct tab_table *t, unsigned flags)
784 /* Easy, type-safe way to submit a tab table to som. */
786 tab_submit (struct tab_table *t)
791 s.class = &tab_table_class;
800 /* Set table row and column offsets for all functions that affect
803 tab_offset (struct tab_table *t, int col, int row)
809 if (row < -1 || row > t->nr)
811 printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
814 if (col < -1 || col > t->nc)
816 printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
822 diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
824 diff += (col - t->col_ofs), t->col_ofs = col;
830 /* Increment the row offset by one. If the table is too small,
831 increase its size. */
833 tab_next_row (struct tab_table *t)
838 if (++t->row_ofs >= t->nr)
839 tab_realloc (t, -1, t->nr * 4 / 3);
842 static struct tab_table *t;
843 static struct outp_driver *d;
846 /* Set the current table to TABLE. */
848 tabi_table (struct som_entity *table)
850 assert (table != NULL);
851 assert (table->type == SOM_TABLE);
854 tab_offset (t, 0, 0);
856 assert (t->w == NULL && t->h == NULL);
857 t->w = pool_nalloc (t->container, t->nc, sizeof *t->w);
858 t->h = pool_nalloc (t->container, t->nr, sizeof *t->h);
859 t->hrh = pool_nmalloc (t->container, t->nr + 1, sizeof *t->hrh);
860 t->wrv = pool_nmalloc (t->container, t->nc + 1, sizeof *t->wrv);
863 /* Returns the line style to use for spacing purposes for a rule
864 of the given TYPE. */
865 static enum outp_line_style
866 rule_to_spacing_type (unsigned char type)
874 return OUTP_L_SINGLE;
876 return OUTP_L_DOUBLE;
882 /* Set the current output device to DRIVER. */
884 tabi_driver (struct outp_driver *driver)
889 assert (driver != NULL);
892 /* Figure out sizes of rules. */
893 for (r = 0; r <= t->nr; r++)
896 for (c = 0; c < t->nc; c++)
898 unsigned char rh = t->rh[c + r * t->cf];
899 int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
906 for (c = 0; c <= t->nc; c++)
909 for (r = 0; r < t->nr; r++)
911 unsigned char *rv = &t->rv[c + r * (t->cf + 1)];
913 if (*rv == UCHAR_MAX)
914 *rv = c != 0 && c != t->nc ? TAL_GAP : TAL_0;
915 w = driver->vert_line_width[rule_to_spacing_type (*rv)];
923 for (i = 0; i < t->nr; i++)
925 for (i = 0; i < t->nc; i++)
929 assert (t->dim != NULL);
936 for (i = 0; i < t->nr; i++)
940 printf ("Table row %d height not initialized.\n", i);
943 assert (t->h[i] > 0);
946 for (i = 0; i < t->nc; i++)
950 printf ("Table column %d width not initialized.\n", i);
953 assert (t->w[i] > 0);
958 /* Add up header sizes. */
959 for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
960 t->wl += t->w[i] + t->wrv[i + 1];
961 for (i = 0, t->ht = t->hrh[0]; i < t->t; i++)
962 t->ht += t->h[i] + t->hrh[i + 1];
963 for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++)
964 t->wr += t->w[i] + t->wrv[i + 1];
965 for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
966 t->hb += t->h[i] + t->hrh[i + 1];
969 if (!(t->flags & SOMF_NO_TITLE))
970 t->ht += d->font_height;
973 /* Return the number of columns and rows in the table into N_COLUMNS
974 and N_ROWS, respectively. */
976 tabi_count (int *n_columns, int *n_rows)
978 assert (n_columns != NULL && n_rows != NULL);
983 static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual);
985 /* Return the horizontal and vertical size of the entire table,
986 including headers, for the current output device, into HORIZ and
989 tabi_area (int *horiz, int *vert)
991 assert (horiz != NULL && vert != NULL);
996 for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
997 c < t->nc - t->r; c++)
998 w += t->w[c] + t->wrv[c];
1004 for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
1005 r < t->nr - t->b; r++)
1006 h += t->h[r] + t->hrh[r];
1011 /* Return the column style for this table into STYLE. */
1013 tabi_columns (int *style)
1015 assert (style != NULL);
1016 *style = t->col_style;
1019 /* Return the number of header rows/columns on the left, right, top,
1020 and bottom sides into HL, HR, HT, and HB, respectively. */
1022 tabi_headers (int *hl, int *hr, int *ht, int *hb)
1024 assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL);
1031 /* Determines the number of rows or columns (including appropriate
1032 headers), depending on CUMTYPE, that will fit into the space
1033 specified. Takes rows/columns starting at index START and attempts
1034 to fill up available space MAX. Returns in END the index of the
1035 last row/column plus one; returns in ACTUAL the actual amount of
1036 space the selected rows/columns (including appropriate headers)
1039 tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
1046 assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1047 if (cumtype == SOM_ROWS)
1049 assert (start >= 0 && start < t->nr);
1052 r = &t->hrh[start + 1];
1053 total = t->ht + t->hb;
1057 assert (start >= 0 && start < t->nc);
1060 r = &t->wrv[start + 1];
1061 total = t->wl + t->wr;
1077 for (x = start + 1; x < n; x++)
1079 int amt = *d++ + *r++;
1097 /* Return flags set for the current table into FLAGS. */
1099 tabi_flags (unsigned *flags)
1101 assert (flags != NULL);
1105 /* Returns true if the table will fit in the given page WIDTH,
1108 tabi_fits_width (int width)
1112 for (i = t->l; i < t->nc - t->r; i++)
1113 if (t->wl + t->wr + t->w[i] > width)
1119 /* Returns true if the table will fit in the given page LENGTH,
1122 tabi_fits_length (int length)
1126 for (i = t->t; i < t->nr - t->b; i++)
1127 if (t->ht + t->hb + t->h[i] > length)
1133 /* Sets the number of header rows/columns on the left, right, top,
1134 and bottom sides to HL, HR, HT, and HB, respectively. */
1136 tabi_set_headers (int hl, int hr, int ht, int hb)
1144 /* Render title for current table, with major index X and minor index
1145 Y. Y may be zero, or X and Y may be zero, but X should be nonzero
1148 tabi_title (int x, int y)
1153 if (t->flags & SOMF_NO_TITLE)
1156 cp = spprintf (buf, "%d.%d", table_num, subtable_num);
1158 cp = spprintf (cp, "(%d:%d)", x, y);
1160 cp = spprintf (cp, "(%d)", x);
1161 if (command_name != NULL)
1162 cp = spprintf (cp, " %s", command_name);
1163 cp = stpcpy (cp, ". ");
1164 if (t->title != NULL)
1166 size_t length = strlen (t->title);
1167 memcpy (cp, t->title, length);
1173 struct outp_text text;
1175 text.font = OUTP_PROPORTIONAL;
1176 text.justification = OUTP_LEFT;
1177 text.string = ss_buffer (buf, cp - buf);
1179 text.v = d->font_height;
1182 d->class->text_draw (d, &text);
1186 static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
1188 /* Renders columns C0...C1, plus headers, of rows R0...R1,
1189 at the given vertical position Y.
1190 C0 and C1 count vertical rules as columns,
1191 but R0 and R1 do not count horizontal rules as rows.
1192 Returns the vertical position after rendering. */
1194 render_rows (int y, int c0, int c1, int r0, int r1)
1197 for (r = r0; r < r1; r++)
1200 x = render_strip (x, y, r, 0, t->l * 2 + 1, r0, r1);
1201 x = render_strip (x, y, r, c0 * 2 + 1, c1 * 2, r0, r1);
1202 x = render_strip (x, y, r, (t->nc - t->r) * 2, t->nc * 2 + 1, r0, r1);
1203 y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2];
1208 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1209 current position on the current output device. */
1211 tabi_render (int c0, int r0, int c1, int r1)
1218 if (!(t->flags & SOMF_NO_TITLE))
1219 y += d->font_height;
1221 y = render_rows (y, c0, c1, 0, t->t * 2 + 1);
1222 y = render_rows (y, c0, c1, r0 * 2 + 1, r1 * 2);
1223 y = render_rows (y, c0, c1, (t->nr - t->b) * 2, t->nr * 2 + 1);
1226 const struct som_table_class tab_table_class =
1252 static enum outp_justification
1253 translate_justification (unsigned int opt)
1255 switch (opt & TAB_ALIGN_MASK)
1268 /* Returns the line style to use for drawing a rule of the given
1270 static enum outp_line_style
1271 rule_to_draw_type (unsigned char type)
1279 return OUTP_L_SINGLE;
1281 return OUTP_L_DOUBLE;
1287 /* Returns the horizontal rule at the given column and row. */
1289 get_hrule (int c, int r)
1291 return t->rh[c + r * t->cf];
1294 /* Returns the vertical rule at the given column and row. */
1296 get_vrule (int c, int r)
1298 return t->rv[c + r * (t->cf + 1)];
1301 /* Renders the horizontal rule at the given column and row
1302 at (X,Y) on the page. */
1304 render_horz_rule (int x, int y, int c, int r)
1306 enum outp_line_style style = rule_to_draw_type (get_hrule (c, r));
1307 if (style != OUTP_L_NONE)
1308 d->class->line (d, x, y, x + t->w[c], y + t->hrh[r],
1309 OUTP_L_NONE, style, OUTP_L_NONE, style);
1312 /* Renders the vertical rule at the given column and row
1313 at (X,Y) on the page. */
1315 render_vert_rule (int x, int y, int c, int r)
1317 enum outp_line_style style = rule_to_draw_type (get_vrule (c, r));
1318 if (style != OUTP_L_NONE)
1319 d->class->line (d, x, y, x + t->wrv[c], y + t->h[r],
1320 style, OUTP_L_NONE, style, OUTP_L_NONE);
1323 /* Renders the rule intersection at the given column and row
1324 at (X,Y) on the page. */
1326 render_rule_intersection (int x, int y, int c, int r)
1328 /* Bounds of intersection. */
1331 int x1 = x + t->wrv[c];
1332 int y1 = y + t->hrh[r];
1334 /* Lines on each side of intersection. */
1335 int top = r > 0 ? get_vrule (c, r - 1) : TAL_0;
1336 int left = c > 0 ? get_hrule (c - 1, r) : TAL_0;
1337 int bottom = r < t->nr ? get_vrule (c, r) : TAL_0;
1338 int right = c < t->nc ? get_hrule (c, r) : TAL_0;
1340 /* Output style for each line. */
1341 enum outp_line_style o_top = rule_to_draw_type (top);
1342 enum outp_line_style o_left = rule_to_draw_type (left);
1343 enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1344 enum outp_line_style o_right = rule_to_draw_type (right);
1346 if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1347 || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1348 d->class->line (d, x0, y0, x1, y1, o_top, o_left, o_bottom, o_right);
1351 /* Returns the width of columns C1...C2 exclusive,
1352 including interior but not exterior rules. */
1354 strip_width (int c1, int c2)
1359 for (c = c1; c < c2; c++)
1360 width += t->w[c] + t->wrv[c + 1];
1362 width -= t->wrv[c2];
1366 /* Returns the height of rows R1...R2 exclusive,
1367 including interior but not exterior rules. */
1369 strip_height (int r1, int r2)
1374 for (r = r1; r < r2; r++)
1375 height += t->h[r] + t->hrh[r + 1];
1377 height -= t->hrh[r2];
1381 /* Renders the cell at the given column and row at (X,Y) on the
1382 page. Also renders joined cells that extend as far to the
1383 right as C1 and as far down as R1. */
1385 render_cell (int x, int y, int c, int r, int c1, int r1)
1387 const int index = c + (r * t->cf);
1388 unsigned char type = t->ct[index];
1389 struct substring *content = &t->cc[index];
1391 if (!(type & TAB_JOIN))
1393 if (!(type & TAB_EMPTY))
1395 struct outp_text text;
1396 text.font = options_to_font (type);
1397 text.justification = translate_justification (type);
1398 text.string = *content;
1403 d->class->text_draw (d, &text);
1408 struct tab_joined_cell *j
1409 = (struct tab_joined_cell *) ss_data (*content);
1411 if (j->hit != tab_hit)
1415 if (j->x1 == c && j->y1 == r)
1417 struct outp_text text;
1418 text.font = options_to_font (type);
1419 text.justification = translate_justification (type);
1420 text.string = j->contents;
1423 text.h = strip_width (j->x1, MIN (j->x2, c1));
1424 text.v = strip_height (j->y1, MIN (j->y2, r1));
1425 d->class->text_draw (d, &text);
1431 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1432 on row R, at (X,Y). Returns X position after rendering.
1433 Also renders joined cells that extend beyond that strip,
1434 cropping them to lie within rendering region (C0,R0)-(C1,R1).
1435 C0 and C1 count vertical rules as columns.
1436 R counts horizontal rules as rows, but R0 and R1 do not. */
1438 render_strip (int x, int y, int r, int c0, int c1, int r0 UNUSED, int r1)
1442 for (c = c0; c < c1; c++)
1446 render_cell (x, y, c / 2, r / 2, c1 / 2, r1);
1448 render_horz_rule (x, y, c / 2, r / 2);
1454 render_vert_rule (x, y, c / 2, r / 2);
1456 render_rule_intersection (x, y, c / 2, r / 2);
1463 /* Sets COMMAND_NAME as the name of the current command,
1464 for embedding in output. */
1466 tab_set_command_name (const char *command_name_)
1468 free (command_name);
1469 command_name = command_name_ ? xstrdup (command_name_) : NULL;