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/assertion.h>
33 #include <libpspp/compiler.h>
34 #include <libpspp/misc.h>
35 #include <libpspp/pool.h>
37 #include <data/settings.h>
43 #define _(msgid) gettext (msgid)
45 const struct som_table_class tab_table_class;
46 static char *command_name;
48 /* Returns the font to use for a cell with the given OPTIONS. */
50 options_to_font (unsigned options)
52 return (options & TAB_FIX ? OUTP_FIXED
53 : options & TAB_EMPH ? OUTP_EMPHASIS
57 /* Creates a table with NC columns and NR rows. */
59 tab_create (int nc, int nr, int reallocable UNUSED)
63 t = pool_create_container (struct tab_table, container);
64 t->col_style = TAB_COL_NONE;
70 t->l = t->r = t->t = t->b = 0;
72 t->cc = pool_nmalloc (t->container, nr * nc, sizeof *t->cc);
73 t->ct = pool_malloc (t->container, nr * nc);
74 memset (t->ct, TAB_EMPTY, nc * nr);
76 t->rh = pool_nmalloc (t->container, nc, nr + 1);
77 memset (t->rh, 0, nc * (nr + 1));
79 t->rv = pool_nmalloc (t->container, nr, nc + 1);
80 memset (t->rv, UCHAR_MAX, nr * (nc + 1));
84 t->col_ofs = t->row_ofs = 0;
89 /* Destroys table T. */
91 tab_destroy (struct tab_table *t)
95 pool_destroy (t->container);
98 /* Sets the width and height of a table, in columns and rows,
99 respectively. Use only to reduce the size of a table, since it
100 does not change the amount of allocated memory. */
102 tab_resize (struct tab_table *t, int nc, int nr)
107 assert (nc + t->col_ofs <= t->cf);
108 t->nc = nc + t->col_ofs;
112 assert (nr + t->row_ofs <= t->nr);
113 t->nr = nr + t->row_ofs;
117 /* Changes either or both dimensions of a table. Consider using the
118 above routine instead if it won't waste a lot of space.
120 Changing the number of columns in a table is particularly expensive
121 in space and time. Avoid doing such. FIXME: In fact, transferring
122 of rules isn't even implemented yet. */
124 tab_realloc (struct tab_table *t, int nc, int nr)
132 tab_offset (t, 0, 0);
139 assert (nc == t->nc);
143 int mr1 = MIN (nr, t->nr);
144 int mc1 = MIN (nc, t->nc);
146 struct substring *new_cc;
147 unsigned char *new_ct;
150 new_cc = pool_nmalloc (t->container, nr * nc, sizeof *new_cc);
151 new_ct = pool_malloc (t->container, nr * nc);
152 for (r = 0; r < mr1; r++)
154 memcpy (&new_cc[r * nc], &t->cc[r * t->nc], mc1 * sizeof *t->cc);
155 memcpy (&new_ct[r * nc], &t->ct[r * t->nc], mc1);
156 memset (&new_ct[r * nc + t->nc], TAB_EMPTY, nc - t->nc);
158 pool_free (t->container, t->cc);
159 pool_free (t->container, t->ct);
164 else if (nr != t->nr)
166 t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc);
167 t->ct = pool_realloc (t->container, t->ct, nr * nc);
169 t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
170 t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
174 memset (&t->rh[nc * (t->nr + 1)], TAL_0, (nr - t->nr) * nc);
175 memset (&t->rv[(nc + 1) * t->nr], UCHAR_MAX,
176 (nr - t->nr) * (nc + 1));
180 memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr));
186 tab_offset (t, co, ro);
189 /* Sets the number of header rows on each side of TABLE to L on the
190 left, R on the right, T on the top, B on the bottom. Header rows
191 are repeated when a table is broken across multiple columns or
194 tab_headers (struct tab_table *table, int l, int r, int t, int b)
196 assert (table != NULL);
197 assert (l < table->nc);
198 assert (r < table->nc);
199 assert (t < table->nr);
200 assert (b < table->nr);
209 /* Set up table T so that, when it is an appropriate size, it will be
210 displayed across the page in columns.
212 STYLE is a TAB_COL_* constant. GROUP is the number of rows to take
215 tab_columns (struct tab_table *t, int style, int group)
218 t->col_style = style;
219 t->col_group = group;
224 /* Draws a vertical line to the left of cells at horizontal position X
225 from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
227 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
232 if (x + t->col_ofs < 0 || x + t->col_ofs > t->nc
233 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
234 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
236 printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
237 "table size (%d,%d)\n"),
238 x, t->col_ofs, x + t->col_ofs,
239 y1, t->row_ofs, y1 + t->row_ofs,
240 y2, t->row_ofs, y2 + t->row_ofs,
254 assert (y2 <= t->nr);
259 for (y = y1; y <= y2; y++)
260 t->rv[x + (t->cf + 1) * y] = style;
264 /* Draws a horizontal line above cells at vertical position Y from X1
265 to X2 inclusive in style STYLE, if style is not -1. */
267 tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
284 for (x = x1; x <= x2; x++)
285 t->rh[x + t->cf * y] = style;
289 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
290 lines of style F_H and vertical lines of style F_V. Fills the
291 interior of the box with horizontal lines of style I_H and vertical
292 lines of style I_V. Any of the line styles may be -1 to avoid
293 drawing those lines. This is distinct from 0, which draws a null
296 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
297 int x1, int y1, int x2, int y2)
302 if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc
303 || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= t->nc
304 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
305 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
307 printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
308 "in table size (%d,%d)\n"),
309 x1, t->col_ofs, x1 + t->col_ofs,
310 y1, t->row_ofs, y1 + t->row_ofs,
311 x2, t->col_ofs, x2 + t->col_ofs,
312 y2, t->row_ofs, y2 + t->row_ofs,
333 for (x = x1; x <= x2; x++)
335 t->rh[x + t->cf * y1] = f_h;
336 t->rh[x + t->cf * (y2 + 1)] = f_h;
342 for (y = y1; y <= y2; y++)
344 t->rv[x1 + (t->cf + 1) * y] = f_v;
345 t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
353 for (y = y1 + 1; y <= y2; y++)
357 for (x = x1; x <= x2; x++)
358 t->rh[x + t->cf * y] = i_h;
365 for (x = x1 + 1; x <= x2; x++)
369 for (y = y1; y <= y2; y++)
370 t->rv[x + (t->cf + 1) * y] = i_v;
375 /* Formats text TEXT and arguments ARGS as indicated in OPT in
376 TABLE's pool and returns the resultant string. */
377 static struct substring
378 text_format (struct tab_table *table, int opt, const char *text, va_list args)
380 assert (table != NULL && text != NULL);
382 return ss_cstr (opt & TAT_PRINTF
383 ? pool_vasprintf (table->container, text, args)
384 : pool_strdup (table->container, text));
387 /* Set the title of table T to TITLE, which is formatted as if
388 passed to printf(). */
390 tab_title (struct tab_table *t, const char *title, ...)
394 assert (t != NULL && title != NULL);
395 va_start (args, title);
396 t->title = xvasprintf (title, args);
400 /* Set DIM_FUNC as the dimension function for table T. */
402 tab_dim (struct tab_table *t, tab_dim_func *dim_func)
404 assert (t != NULL && t->dim == NULL);
408 /* Returns the natural width of column C in table T for driver D, that
409 is, the smallest width necessary to display all its cells without
410 wrapping. The width will be no larger than the page width minus
411 left and right rule widths. */
413 tab_natural_width (struct tab_table *t, struct outp_driver *d, int c)
417 assert (t != NULL && c >= 0 && c < t->nc);
421 for (width = r = 0; r < t->nr; r++)
423 struct outp_text text;
424 unsigned char opt = t->ct[c + r * t->cf];
427 if (opt & (TAB_JOIN | TAB_EMPTY))
430 text.string = t->cc[c + r * t->cf];
431 text.justification = OUTP_LEFT;
432 text.font = options_to_font (opt);
433 text.h = text.v = INT_MAX;
435 d->class->text_metrics (d, &text, &w, NULL);
443 /* FIXME: This is an ugly kluge to compensate for the fact
444 that we don't let joined cells contribute to column
446 width = d->prop_em_width * 8;
450 const int clamp = d->width - t->wrv[0] - t->wrv[t->nc];
459 /* Returns the natural height of row R in table T for driver D, that
460 is, the minimum height necessary to display the information in the
461 cell at the widths set for each column. */
463 tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
467 assert (t != NULL && r >= 0 && r < t->nr);
472 for (height = d->font_height, c = 0; c < t->nc; c++)
474 struct outp_text text;
475 unsigned char opt = t->ct[c + r * t->cf];
478 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_fixed (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_fixed(): 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 value VAL as
596 If FMT is null, then the default print format will be used.
599 tab_double (struct tab_table *table, int c, int r, unsigned char opt,
600 double val, const struct fmt_spec *fmt)
606 union value double_value;
608 assert (table != NULL);
611 assert (c < table->nc);
613 assert (r < table->nr);
616 fmt = settings_get_format ();
618 fmt_check_output (fmt);
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_double(): 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 double_value.f = val;
635 data_out (&double_value, fmt, buf);
638 while (isspace ((unsigned char) *cp) && cp < &buf[fmt->w])
640 w = fmt->w - (cp - buf);
642 contents = pool_alloc (table->container, w);
643 table->cc[c + r * table->cf] = ss_buffer (contents, w);
644 table->ct[c + r * table->cf] = opt;
645 memcpy (contents, cp, w);
649 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
652 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
656 assert (table != NULL && text != NULL);
660 assert (c < table->nc);
661 assert (r < table->nr);
665 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
666 || c + table->col_ofs >= table->nc
667 || r + table->row_ofs >= table->nr)
669 printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
671 c, table->col_ofs, c + table->col_ofs,
672 r, table->row_ofs, r + table->row_ofs,
673 table->nc, table->nr);
678 va_start (args, text);
679 table->cc[c + r * table->cf] = text_format (table, opt, text, args);
680 table->ct[c + r * table->cf] = opt;
684 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
685 options OPT to have text value TEXT. */
687 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
688 unsigned opt, const char *text, ...)
690 struct tab_joined_cell *j;
692 assert (table != NULL && text != NULL);
694 assert (x1 + table->col_ofs >= 0);
695 assert (y1 + table->row_ofs >= 0);
698 assert (y2 + table->row_ofs < table->nr);
699 assert (x2 + table->col_ofs < table->nc);
702 if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
703 || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
704 || x2 < x1 || x2 + table->col_ofs >= table->nc
705 || y2 < y2 || y2 + table->row_ofs >= table->nr)
707 printf ("tab_joint_text(): bad cell "
708 "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
709 x1, table->col_ofs, x1 + table->col_ofs,
710 y1, table->row_ofs, y1 + table->row_ofs,
711 x2, table->col_ofs, x2 + table->col_ofs,
712 y2, table->row_ofs, y2 + table->row_ofs,
713 table->nc, table->nr);
718 tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
720 j = pool_alloc (table->container, sizeof *j);
722 j->x1 = x1 + table->col_ofs;
723 j->y1 = y1 + table->row_ofs;
724 j->x2 = ++x2 + table->col_ofs;
725 j->y2 = ++y2 + table->row_ofs;
730 va_start (args, text);
731 j->contents = text_format (table, opt, text, args);
738 struct substring *cc = &table->cc[x1 + y1 * table->cf];
739 unsigned char *ct = &table->ct[x1 + y1 * table->cf];
740 const int ofs = table->cf - (x2 - x1);
744 for (y = y1; y < y2; y++)
748 for (x = x1; x < x2; x++)
750 *cc++ = ss_buffer ((char *) j, 0);
760 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
762 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
763 struct substring *string)
765 assert (table != NULL && string != NULL);
768 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
769 || c + table->col_ofs >= table->nc
770 || r + table->row_ofs >= table->nr)
772 printf ("tab_raw(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
774 c, table->col_ofs, c + table->col_ofs,
775 r, table->row_ofs, r + table->row_ofs,
776 table->nc, table->nr);
781 table->cc[c + r * table->cf] = *string;
782 table->ct[c + r * table->cf] = opt;
787 /* Sets the widths of all the columns and heights of all the rows in
788 table T for driver D. */
790 nowrap_dim (struct tab_table *t, struct outp_driver *d)
792 t->w[0] = tab_natural_width (t, d, 0);
793 t->h[0] = d->font_height;
796 /* Sets the widths of all the columns and heights of all the rows in
797 table T for driver D. */
799 wrap_dim (struct tab_table *t, struct outp_driver *d)
801 t->w[0] = tab_natural_width (t, d, 0);
802 t->h[0] = tab_natural_height (t, d, 0);
805 /* Outputs text BUF as a table with a single cell having cell options
806 OPTIONS, which is a combination of the TAB_* and TAT_*
809 tab_output_text (int options, const char *buf, ...)
811 struct tab_table *t = tab_create (1, 1, 0);
812 char *tmp_buf = NULL;
814 if (options & TAT_PRINTF)
818 va_start (args, buf);
819 buf = tmp_buf = xvasprintf (buf, args);
823 tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
824 tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
825 tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim);
831 /* Set table flags to FLAGS. */
833 tab_flags (struct tab_table *t, unsigned flags)
839 /* Easy, type-safe way to submit a tab table to som. */
841 tab_submit (struct tab_table *t)
846 s.class = &tab_table_class;
855 /* Set table row and column offsets for all functions that affect
858 tab_offset (struct tab_table *t, int col, int row)
864 if (row < -1 || row > t->nr)
866 printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
869 if (col < -1 || col > t->nc)
871 printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
877 diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
879 diff += (col - t->col_ofs), t->col_ofs = col;
885 /* Increment the row offset by one. If the table is too small,
886 increase its size. */
888 tab_next_row (struct tab_table *t)
893 if (++t->row_ofs >= t->nr)
894 tab_realloc (t, -1, t->nr * 4 / 3);
897 static struct tab_table *t;
898 static struct outp_driver *d;
901 /* Set the current table to TABLE. */
903 tabi_table (struct som_entity *table)
905 assert (table != NULL);
906 assert (table->type == SOM_TABLE);
909 tab_offset (t, 0, 0);
911 assert (t->w == NULL && t->h == NULL);
912 t->w = pool_nalloc (t->container, t->nc, sizeof *t->w);
913 t->h = pool_nalloc (t->container, t->nr, sizeof *t->h);
914 t->hrh = pool_nmalloc (t->container, t->nr + 1, sizeof *t->hrh);
915 t->wrv = pool_nmalloc (t->container, t->nc + 1, sizeof *t->wrv);
918 /* Returns the line style to use for spacing purposes for a rule
919 of the given TYPE. */
920 static enum outp_line_style
921 rule_to_spacing_type (unsigned char type)
929 return OUTP_L_SINGLE;
931 return OUTP_L_DOUBLE;
937 /* Set the current output device to DRIVER. */
939 tabi_driver (struct outp_driver *driver)
944 assert (driver != NULL);
947 /* Figure out sizes of rules. */
948 for (r = 0; r <= t->nr; r++)
951 for (c = 0; c < t->nc; c++)
953 unsigned char rh = t->rh[c + r * t->cf];
954 int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
961 for (c = 0; c <= t->nc; c++)
964 for (r = 0; r < t->nr; r++)
966 unsigned char *rv = &t->rv[c + r * (t->cf + 1)];
968 if (*rv == UCHAR_MAX)
969 *rv = c != 0 && c != t->nc ? TAL_GAP : TAL_0;
970 w = driver->vert_line_width[rule_to_spacing_type (*rv)];
978 for (i = 0; i < t->nr; i++)
980 for (i = 0; i < t->nc; i++)
984 assert (t->dim != NULL);
991 for (i = 0; i < t->nr; i++)
995 printf ("Table row %d height not initialized.\n", i);
998 assert (t->h[i] > 0);
1001 for (i = 0; i < t->nc; i++)
1005 printf ("Table column %d width not initialized.\n", i);
1008 assert (t->w[i] > 0);
1013 /* Add up header sizes. */
1014 for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
1015 t->wl += t->w[i] + t->wrv[i + 1];
1016 for (i = 0, t->ht = t->hrh[0]; i < t->t; i++)
1017 t->ht += t->h[i] + t->hrh[i + 1];
1018 for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++)
1019 t->wr += t->w[i] + t->wrv[i + 1];
1020 for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
1021 t->hb += t->h[i] + t->hrh[i + 1];
1024 if (!(t->flags & SOMF_NO_TITLE))
1025 t->ht += d->font_height;
1028 /* Return the number of columns and rows in the table into N_COLUMNS
1029 and N_ROWS, respectively. */
1031 tabi_count (int *n_columns, int *n_rows)
1033 assert (n_columns != NULL && n_rows != NULL);
1038 static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual);
1040 /* Return the horizontal and vertical size of the entire table,
1041 including headers, for the current output device, into HORIZ and
1044 tabi_area (int *horiz, int *vert)
1046 assert (horiz != NULL && vert != NULL);
1051 for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
1052 c < t->nc - t->r; c++)
1053 w += t->w[c] + t->wrv[c];
1059 for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
1060 r < t->nr - t->b; r++)
1061 h += t->h[r] + t->hrh[r];
1066 /* Return the column style for this table into STYLE. */
1068 tabi_columns (int *style)
1070 assert (style != NULL);
1071 *style = t->col_style;
1074 /* Return the number of header rows/columns on the left, right, top,
1075 and bottom sides into HL, HR, HT, and HB, respectively. */
1077 tabi_headers (int *hl, int *hr, int *ht, int *hb)
1079 assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL);
1086 /* Determines the number of rows or columns (including appropriate
1087 headers), depending on CUMTYPE, that will fit into the space
1088 specified. Takes rows/columns starting at index START and attempts
1089 to fill up available space MAX. Returns in END the index of the
1090 last row/column plus one; returns in ACTUAL the actual amount of
1091 space the selected rows/columns (including appropriate headers)
1094 tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
1101 assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1102 if (cumtype == SOM_ROWS)
1104 assert (start >= 0 && start < t->nr);
1107 r = &t->hrh[start + 1];
1108 total = t->ht + t->hb;
1112 assert (start >= 0 && start < t->nc);
1115 r = &t->wrv[start + 1];
1116 total = t->wl + t->wr;
1132 for (x = start + 1; x < n; x++)
1134 int amt = *d++ + *r++;
1152 /* Return flags set for the current table into FLAGS. */
1154 tabi_flags (unsigned *flags)
1156 assert (flags != NULL);
1160 /* Returns true if the table will fit in the given page WIDTH,
1163 tabi_fits_width (int width)
1167 for (i = t->l; i < t->nc - t->r; i++)
1168 if (t->wl + t->wr + t->w[i] > width)
1174 /* Returns true if the table will fit in the given page LENGTH,
1177 tabi_fits_length (int length)
1181 for (i = t->t; i < t->nr - t->b; i++)
1182 if (t->ht + t->hb + t->h[i] > length)
1188 /* Sets the number of header rows/columns on the left, right, top,
1189 and bottom sides to HL, HR, HT, and HB, respectively. */
1191 tabi_set_headers (int hl, int hr, int ht, int hb)
1199 /* Render title for current table, with major index X and minor index
1200 Y. Y may be zero, or X and Y may be zero, but X should be nonzero
1203 tabi_title (int x, int y)
1208 if (t->flags & SOMF_NO_TITLE)
1211 cp = spprintf (buf, "%d.%d", table_num, subtable_num);
1213 cp = spprintf (cp, "(%d:%d)", x, y);
1215 cp = spprintf (cp, "(%d)", x);
1216 if (command_name != NULL)
1217 cp = spprintf (cp, " %s", command_name);
1218 cp = stpcpy (cp, ". ");
1219 if (t->title != NULL)
1221 size_t length = strlen (t->title);
1222 memcpy (cp, t->title, length);
1228 struct outp_text text;
1230 text.font = OUTP_PROPORTIONAL;
1231 text.justification = OUTP_LEFT;
1232 text.string = ss_buffer (buf, cp - buf);
1234 text.v = d->font_height;
1237 d->class->text_draw (d, &text);
1241 static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
1243 /* Renders columns C0...C1, plus headers, of rows R0...R1,
1244 at the given vertical position Y.
1245 C0 and C1 count vertical rules as columns,
1246 but R0 and R1 do not count horizontal rules as rows.
1247 Returns the vertical position after rendering. */
1249 render_rows (int y, int c0, int c1, int r0, int r1)
1252 for (r = r0; r < r1; r++)
1255 x = render_strip (x, y, r, 0, t->l * 2 + 1, r0, r1);
1256 x = render_strip (x, y, r, c0 * 2 + 1, c1 * 2, r0, r1);
1257 x = render_strip (x, y, r, (t->nc - t->r) * 2, t->nc * 2 + 1, r0, r1);
1258 y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2];
1263 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1264 current position on the current output device. */
1266 tabi_render (int c0, int r0, int c1, int r1)
1273 if (!(t->flags & SOMF_NO_TITLE))
1274 y += d->font_height;
1276 y = render_rows (y, c0, c1, 0, t->t * 2 + 1);
1277 y = render_rows (y, c0, c1, r0 * 2 + 1, r1 * 2);
1278 y = render_rows (y, c0, c1, (t->nr - t->b) * 2, t->nr * 2 + 1);
1281 const struct som_table_class tab_table_class =
1307 static enum outp_justification
1308 translate_justification (unsigned int opt)
1310 switch (opt & TAB_ALIGN_MASK)
1323 /* Returns the line style to use for drawing a rule of the given
1325 static enum outp_line_style
1326 rule_to_draw_type (unsigned char type)
1334 return OUTP_L_SINGLE;
1336 return OUTP_L_DOUBLE;
1342 /* Returns the horizontal rule at the given column and row. */
1344 get_hrule (int c, int r)
1346 return t->rh[c + r * t->cf];
1349 /* Returns the vertical rule at the given column and row. */
1351 get_vrule (int c, int r)
1353 return t->rv[c + r * (t->cf + 1)];
1356 /* Renders the horizontal rule at the given column and row
1357 at (X,Y) on the page. */
1359 render_horz_rule (int x, int y, int c, int r)
1361 enum outp_line_style style = rule_to_draw_type (get_hrule (c, r));
1362 if (style != OUTP_L_NONE)
1363 d->class->line (d, x, y, x + t->w[c], y + t->hrh[r],
1364 OUTP_L_NONE, style, OUTP_L_NONE, style);
1367 /* Renders the vertical rule at the given column and row
1368 at (X,Y) on the page. */
1370 render_vert_rule (int x, int y, int c, int r)
1372 enum outp_line_style style = rule_to_draw_type (get_vrule (c, r));
1373 if (style != OUTP_L_NONE)
1374 d->class->line (d, x, y, x + t->wrv[c], y + t->h[r],
1375 style, OUTP_L_NONE, style, OUTP_L_NONE);
1378 /* Renders the rule intersection at the given column and row
1379 at (X,Y) on the page. */
1381 render_rule_intersection (int x, int y, int c, int r)
1383 /* Bounds of intersection. */
1386 int x1 = x + t->wrv[c];
1387 int y1 = y + t->hrh[r];
1389 /* Lines on each side of intersection. */
1390 int top = r > 0 ? get_vrule (c, r - 1) : TAL_0;
1391 int left = c > 0 ? get_hrule (c - 1, r) : TAL_0;
1392 int bottom = r < t->nr ? get_vrule (c, r) : TAL_0;
1393 int right = c < t->nc ? get_hrule (c, r) : TAL_0;
1395 /* Output style for each line. */
1396 enum outp_line_style o_top = rule_to_draw_type (top);
1397 enum outp_line_style o_left = rule_to_draw_type (left);
1398 enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1399 enum outp_line_style o_right = rule_to_draw_type (right);
1401 if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1402 || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1403 d->class->line (d, x0, y0, x1, y1, o_top, o_left, o_bottom, o_right);
1406 /* Returns the width of columns C1...C2 exclusive,
1407 including interior but not exterior rules. */
1409 strip_width (int c1, int c2)
1414 for (c = c1; c < c2; c++)
1415 width += t->w[c] + t->wrv[c + 1];
1417 width -= t->wrv[c2];
1421 /* Returns the height of rows R1...R2 exclusive,
1422 including interior but not exterior rules. */
1424 strip_height (int r1, int r2)
1429 for (r = r1; r < r2; r++)
1430 height += t->h[r] + t->hrh[r + 1];
1432 height -= t->hrh[r2];
1436 /* Renders the cell at the given column and row at (X,Y) on the
1437 page. Also renders joined cells that extend as far to the
1438 right as C1 and as far down as R1. */
1440 render_cell (int x, int y, int c, int r, int c1, int r1)
1442 const int index = c + (r * t->cf);
1443 unsigned char type = t->ct[index];
1444 struct substring *content = &t->cc[index];
1446 if (!(type & TAB_JOIN))
1448 if (!(type & TAB_EMPTY))
1450 struct outp_text text;
1451 text.font = options_to_font (type);
1452 text.justification = translate_justification (type);
1453 text.string = *content;
1458 d->class->text_draw (d, &text);
1463 struct tab_joined_cell *j
1464 = (struct tab_joined_cell *) ss_data (*content);
1466 if (j->hit != tab_hit)
1470 if (j->x1 == c && j->y1 == r)
1472 struct outp_text text;
1473 text.font = options_to_font (type);
1474 text.justification = translate_justification (type);
1475 text.string = j->contents;
1478 text.h = strip_width (j->x1, MIN (j->x2, c1));
1479 text.v = strip_height (j->y1, MIN (j->y2, r1));
1480 d->class->text_draw (d, &text);
1486 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1487 on row R, at (X,Y). Returns X position after rendering.
1488 Also renders joined cells that extend beyond that strip,
1489 cropping them to lie within rendering region (C0,R0)-(C1,R1).
1490 C0 and C1 count vertical rules as columns.
1491 R counts horizontal rules as rows, but R0 and R1 do not. */
1493 render_strip (int x, int y, int r, int c0, int c1, int r0 UNUSED, int r1)
1497 for (c = c0; c < c1; c++)
1501 render_cell (x, y, c / 2, r / 2, c1 / 2, r1);
1503 render_horz_rule (x, y, c / 2, r / 2);
1509 render_vert_rule (x, y, c / 2, r / 2);
1511 render_rule_intersection (x, y, c / 2, r / 2);
1518 /* Sets COMMAND_NAME as the name of the current command,
1519 for embedding in output. */
1521 tab_set_command_name (const char *command_name_)
1523 free (command_name);
1524 command_name = command_name_ ? xstrdup (command_name_) : NULL;