1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2006, 2009 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 <data/dictionary.h>
33 #include <libpspp/assertion.h>
34 #include <libpspp/compiler.h>
35 #include <libpspp/misc.h>
36 #include <libpspp/pool.h>
38 #include <data/settings.h>
44 #define _(msgid) gettext (msgid)
46 const struct som_table_class tab_table_class;
47 static char *command_name;
49 /* Returns the font to use for a cell with the given OPTIONS. */
51 options_to_font (unsigned options)
53 return (options & TAB_FIX ? OUTP_FIXED
54 : options & TAB_EMPH ? OUTP_EMPHASIS
58 /* Creates a table with NC columns and NR rows. */
60 tab_create (int nc, int nr, int reallocable UNUSED)
64 t = pool_create_container (struct tab_table, container);
65 t->col_style = TAB_COL_NONE;
71 t->l = t->r = t->t = t->b = 0;
73 t->cc = pool_nmalloc (t->container, nr * nc, sizeof *t->cc);
74 t->ct = pool_malloc (t->container, nr * nc);
75 memset (t->ct, TAB_EMPTY, nc * nr);
77 t->rh = pool_nmalloc (t->container, nc, nr + 1);
78 memset (t->rh, 0, nc * (nr + 1));
80 t->rv = pool_nmalloc (t->container, nr, nc + 1);
81 memset (t->rv, UCHAR_MAX, nr * (nc + 1));
85 t->col_ofs = t->row_ofs = 0;
90 /* Destroys table T. */
92 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 substring *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 in
377 TABLE's pool and returns the resultant string. */
378 static struct substring
379 text_format (struct tab_table *table, int opt, const char *text, va_list args)
381 assert (table != NULL && text != NULL);
383 return ss_cstr (opt & TAT_PRINTF
384 ? pool_vasprintf (table->container, text, args)
385 : pool_strdup (table->container, text));
388 /* Set the title of table T to TITLE, which is formatted as if
389 passed to printf(). */
391 tab_title (struct tab_table *t, const char *title, ...)
395 assert (t != NULL && title != NULL);
396 va_start (args, title);
397 t->title = xvasprintf (title, args);
401 /* Set DIM_FUNC as the dimension function for table T. */
403 tab_dim (struct tab_table *t, tab_dim_func *dim_func, void *aux)
405 assert (t != NULL && t->dim == NULL);
410 /* Returns the natural width of column C in table T for driver D, that
411 is, the smallest width necessary to display all its cells without
412 wrapping. The width will be no larger than the page width minus
413 left and right rule widths. */
415 tab_natural_width (struct tab_table *t, struct outp_driver *d, int c)
419 assert (t != NULL && c >= 0 && c < t->nc);
423 for (width = r = 0; r < t->nr; r++)
425 struct outp_text text;
426 unsigned char opt = t->ct[c + r * t->cf];
429 if (opt & (TAB_JOIN | TAB_EMPTY))
432 text.string = t->cc[c + r * t->cf];
433 text.justification = OUTP_LEFT;
434 text.font = options_to_font (opt);
435 text.h = text.v = INT_MAX;
437 d->class->text_metrics (d, &text, &w, NULL);
445 /* FIXME: This is an ugly kluge to compensate for the fact
446 that we don't let joined cells contribute to column
448 width = d->prop_em_width * 8;
452 const int clamp = d->width - t->wrv[0] - t->wrv[t->nc];
461 /* Returns the natural height of row R in table T for driver D, that
462 is, the minimum height necessary to display the information in the
463 cell at the widths set for each column. */
465 tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
469 assert (t != NULL && r >= 0 && r < t->nr);
474 for (height = d->font_height, c = 0; c < t->nc; c++)
476 struct outp_text text;
477 unsigned char opt = t->ct[c + r * t->cf];
480 if (opt & (TAB_JOIN | TAB_EMPTY))
483 text.string = t->cc[c + r * t->cf];
484 text.justification = OUTP_LEFT;
485 text.font = options_to_font (opt);
488 d->class->text_metrics (d, &text, NULL, &h);
498 /* Callback function to set all columns and rows to their natural
499 dimensions. Not really meant to be called directly. */
501 tab_natural_dimensions (struct tab_table *t, struct outp_driver *d,
508 for (i = 0; i < t->nc; i++)
509 t->w[i] = tab_natural_width (t, d, i);
511 for (i = 0; i < t->nr; i++)
512 t->h[i] = tab_natural_height (t, d, i);
518 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
519 from V, displayed with format spec F. */
521 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
522 const union value *v, const struct dictionary *dict,
523 const struct fmt_spec *f)
527 assert (table != NULL && v != NULL && f != NULL);
529 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
530 || c + table->col_ofs >= table->nc
531 || r + table->row_ofs >= table->nr)
533 printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
535 c, table->col_ofs, c + table->col_ofs,
536 r, table->row_ofs, r + table->row_ofs,
537 table->nc, table->nr);
542 contents = data_out_pool (v, dict_get_encoding (dict), f, table->container);
544 table->cc[c + r * table->cf] = ss_cstr (contents);
545 table->ct[c + r * table->cf] = opt;
548 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
549 with NDEC decimal places. */
551 tab_fixed (struct tab_table *table, int c, int r, unsigned char opt,
552 double val, int w, int d)
557 union value double_value;
559 assert (table != NULL && w <= 40);
562 assert (c < table->nc);
564 assert (r < table->nr);
566 f = fmt_for_output (FMT_F, w, d);
569 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
570 || c + table->col_ofs >= table->nc
571 || r + table->row_ofs >= table->nr)
573 printf ("tab_fixed(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
575 c, table->col_ofs, c + table->col_ofs,
576 r, table->row_ofs, r + table->row_ofs,
577 table->nc, table->nr);
582 double_value.f = val;
583 s = data_out_pool (&double_value, LEGACY_NATIVE, &f, table->container);
586 while (isspace ((unsigned char) *cp) && cp < &s[w])
590 table->cc[c + r * table->cf] = ss_buffer (cp, f.w);
591 table->ct[c + r * table->cf] = opt;
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)
605 union value double_value ;
607 assert (table != NULL);
610 assert (c < table->nc);
612 assert (r < table->nr);
615 fmt = settings_get_format ();
617 fmt_check_output (fmt);
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_double(): 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 double_value.f = val;
634 s = data_out_pool (&double_value, LEGACY_NATIVE, fmt, table->container);
637 while (isspace ((unsigned char) *cp) && cp < s + fmt->w)
641 w = fmt->w - (cp - s);
643 table->cc[c + r * table->cf] = ss_buffer (cp, w);
644 table->ct[c + r * table->cf] = opt;
648 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
651 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
655 assert (table != NULL && text != NULL);
659 assert (c < table->nc);
660 assert (r < table->nr);
664 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
665 || c + table->col_ofs >= table->nc
666 || r + table->row_ofs >= table->nr)
668 printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
670 c, table->col_ofs, c + table->col_ofs,
671 r, table->row_ofs, r + table->row_ofs,
672 table->nc, table->nr);
677 va_start (args, text);
678 table->cc[c + r * table->cf] = text_format (table, opt, text, args);
679 table->ct[c + r * table->cf] = opt;
683 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
684 options OPT to have text value TEXT. */
686 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
687 unsigned opt, const char *text, ...)
689 struct tab_joined_cell *j;
691 assert (table != NULL && text != NULL);
693 assert (x1 + table->col_ofs >= 0);
694 assert (y1 + table->row_ofs >= 0);
697 assert (y2 + table->row_ofs < table->nr);
698 assert (x2 + table->col_ofs < table->nc);
701 if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
702 || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
703 || x2 < x1 || x2 + table->col_ofs >= table->nc
704 || y2 < y2 || y2 + table->row_ofs >= table->nr)
706 printf ("tab_joint_text(): bad cell "
707 "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
708 x1, table->col_ofs, x1 + table->col_ofs,
709 y1, table->row_ofs, y1 + table->row_ofs,
710 x2, table->col_ofs, x2 + table->col_ofs,
711 y2, table->row_ofs, y2 + table->row_ofs,
712 table->nc, table->nr);
717 tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
719 j = pool_alloc (table->container, sizeof *j);
721 j->x1 = x1 + table->col_ofs;
722 j->y1 = y1 + table->row_ofs;
723 j->x2 = ++x2 + table->col_ofs;
724 j->y2 = ++y2 + table->row_ofs;
729 va_start (args, text);
730 j->contents = text_format (table, opt, text, args);
737 struct substring *cc = &table->cc[x1 + y1 * table->cf];
738 unsigned char *ct = &table->ct[x1 + y1 * table->cf];
739 const int ofs = table->cf - (x2 - x1);
743 for (y = y1; y < y2; y++)
747 for (x = x1; x < x2; x++)
749 *cc++ = ss_buffer ((char *) j, 0);
759 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
761 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
762 struct substring *string)
764 assert (table != NULL && string != NULL);
767 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
768 || c + table->col_ofs >= table->nc
769 || r + table->row_ofs >= table->nr)
771 printf ("tab_raw(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
773 c, table->col_ofs, c + table->col_ofs,
774 r, table->row_ofs, r + table->row_ofs,
775 table->nc, table->nr);
780 table->cc[c + r * table->cf] = *string;
781 table->ct[c + r * table->cf] = opt;
786 /* Sets the widths of all the columns and heights of all the rows in
787 table T for driver D. */
789 nowrap_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
791 t->w[0] = tab_natural_width (t, d, 0);
792 t->h[0] = d->font_height;
795 /* Sets the widths of all the columns and heights of all the rows in
796 table T for driver D. */
798 wrap_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
800 t->w[0] = tab_natural_width (t, d, 0);
801 t->h[0] = tab_natural_height (t, d, 0);
804 /* Outputs text BUF as a table with a single cell having cell options
805 OPTIONS, which is a combination of the TAB_* and TAT_*
808 tab_output_text (int options, const char *buf, ...)
810 struct tab_table *t = tab_create (1, 1, 0);
811 char *tmp_buf = NULL;
813 if (options & TAT_PRINTF)
817 va_start (args, buf);
818 buf = tmp_buf = xvasprintf (buf, args);
822 tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
823 tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
824 tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL);
830 /* Set table flags to FLAGS. */
832 tab_flags (struct tab_table *t, unsigned flags)
838 /* Easy, type-safe way to submit a tab table to som. */
840 tab_submit (struct tab_table *t)
845 s.class = &tab_table_class;
854 /* Set table row and column offsets for all functions that affect
857 tab_offset (struct tab_table *t, int col, int row)
863 if (row < -1 || row > t->nr)
865 printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
868 if (col < -1 || col > t->nc)
870 printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
876 diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
878 diff += (col - t->col_ofs), t->col_ofs = col;
884 /* Increment the row offset by one. If the table is too small,
885 increase its size. */
887 tab_next_row (struct tab_table *t)
892 if (++t->row_ofs >= t->nr)
893 tab_realloc (t, -1, t->nr * 4 / 3);
896 static struct tab_table *t;
897 static struct outp_driver *d;
900 /* Set the current table to TABLE. */
902 tabi_table (struct som_entity *table)
904 assert (table != NULL);
905 assert (table->type == SOM_TABLE);
908 tab_offset (t, 0, 0);
910 assert (t->w == NULL && t->h == NULL);
911 t->w = pool_nalloc (t->container, t->nc, sizeof *t->w);
912 t->h = pool_nalloc (t->container, t->nr, sizeof *t->h);
913 t->hrh = pool_nmalloc (t->container, t->nr + 1, sizeof *t->hrh);
914 t->wrv = pool_nmalloc (t->container, t->nc + 1, sizeof *t->wrv);
917 /* Returns the line style to use for spacing purposes for a rule
918 of the given TYPE. */
919 static enum outp_line_style
920 rule_to_spacing_type (unsigned char type)
928 return OUTP_L_SINGLE;
930 return OUTP_L_DOUBLE;
936 /* Set the current output device to DRIVER. */
938 tabi_driver (struct outp_driver *driver)
943 assert (driver != NULL);
946 /* Figure out sizes of rules. */
947 for (r = 0; r <= t->nr; r++)
950 for (c = 0; c < t->nc; c++)
952 unsigned char rh = t->rh[c + r * t->cf];
953 int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
960 for (c = 0; c <= t->nc; c++)
963 for (r = 0; r < t->nr; r++)
965 unsigned char *rv = &t->rv[c + r * (t->cf + 1)];
967 if (*rv == UCHAR_MAX)
968 *rv = c != 0 && c != t->nc ? TAL_GAP : TAL_0;
969 w = driver->vert_line_width[rule_to_spacing_type (*rv)];
977 for (i = 0; i < t->nr; i++)
979 for (i = 0; i < t->nc; i++)
983 assert (t->dim != NULL);
984 t->dim (t, d, t->dim_aux);
990 for (i = 0; i < t->nr; i++)
994 printf ("Table row %d height not initialized.\n", i);
997 assert (t->h[i] > 0);
1000 for (i = 0; i < t->nc; i++)
1004 printf ("Table column %d width not initialized.\n", i);
1007 assert (t->w[i] > 0);
1012 /* Add up header sizes. */
1013 for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
1014 t->wl += t->w[i] + t->wrv[i + 1];
1015 for (i = 0, t->ht = t->hrh[0]; i < t->t; i++)
1016 t->ht += t->h[i] + t->hrh[i + 1];
1017 for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++)
1018 t->wr += t->w[i] + t->wrv[i + 1];
1019 for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
1020 t->hb += t->h[i] + t->hrh[i + 1];
1023 if (!(t->flags & SOMF_NO_TITLE))
1024 t->ht += d->font_height;
1027 /* Return the number of columns and rows in the table into N_COLUMNS
1028 and N_ROWS, respectively. */
1030 tabi_count (int *n_columns, int *n_rows)
1032 assert (n_columns != NULL && n_rows != NULL);
1037 static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual);
1039 /* Return the horizontal and vertical size of the entire table,
1040 including headers, for the current output device, into HORIZ and
1043 tabi_area (int *horiz, int *vert)
1045 assert (horiz != NULL && vert != NULL);
1050 for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
1051 c < t->nc - t->r; c++)
1052 w += t->w[c] + t->wrv[c];
1058 for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
1059 r < t->nr - t->b; r++)
1060 h += t->h[r] + t->hrh[r];
1065 /* Return the column style for this table into STYLE. */
1067 tabi_columns (int *style)
1069 assert (style != NULL);
1070 *style = t->col_style;
1073 /* Return the number of header rows/columns on the left, right, top,
1074 and bottom sides into HL, HR, HT, and HB, respectively. */
1076 tabi_headers (int *hl, int *hr, int *ht, int *hb)
1078 assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL);
1085 /* Determines the number of rows or columns (including appropriate
1086 headers), depending on CUMTYPE, that will fit into the space
1087 specified. Takes rows/columns starting at index START and attempts
1088 to fill up available space MAX. Returns in END the index of the
1089 last row/column plus one; returns in ACTUAL the actual amount of
1090 space the selected rows/columns (including appropriate headers)
1093 tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
1100 assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1101 if (cumtype == SOM_ROWS)
1103 assert (start >= 0 && start < t->nr);
1106 r = &t->hrh[start + 1];
1107 total = t->ht + t->hb;
1111 assert (start >= 0 && start < t->nc);
1114 r = &t->wrv[start + 1];
1115 total = t->wl + t->wr;
1131 for (x = start + 1; x < n; x++)
1133 int amt = *d++ + *r++;
1151 /* Return flags set for the current table into FLAGS. */
1153 tabi_flags (unsigned *flags)
1155 assert (flags != NULL);
1159 /* Returns true if the table will fit in the given page WIDTH,
1162 tabi_fits_width (int width)
1166 for (i = t->l; i < t->nc - t->r; i++)
1167 if (t->wl + t->wr + t->w[i] > width)
1173 /* Returns true if the table will fit in the given page LENGTH,
1176 tabi_fits_length (int length)
1180 for (i = t->t; i < t->nr - t->b; i++)
1181 if (t->ht + t->hb + t->h[i] > length)
1187 /* Sets the number of header rows/columns on the left, right, top,
1188 and bottom sides to HL, HR, HT, and HB, respectively. */
1190 tabi_set_headers (int hl, int hr, int ht, int hb)
1198 /* Render title for current table, with major index X and minor index
1199 Y. Y may be zero, or X and Y may be zero, but X should be nonzero
1202 tabi_title (int x, int y)
1207 if (t->flags & SOMF_NO_TITLE)
1210 cp = spprintf (buf, "%d.%d", table_num, subtable_num);
1212 cp = spprintf (cp, "(%d:%d)", x, y);
1214 cp = spprintf (cp, "(%d)", x);
1215 if (command_name != NULL)
1216 cp = spprintf (cp, " %s", command_name);
1217 cp = stpcpy (cp, ". ");
1218 if (t->title != NULL)
1220 size_t length = strlen (t->title);
1221 memcpy (cp, t->title, length);
1227 struct outp_text text;
1229 text.font = OUTP_PROPORTIONAL;
1230 text.justification = OUTP_LEFT;
1231 text.string = ss_buffer (buf, cp - buf);
1233 text.v = d->font_height;
1236 d->class->text_draw (d, &text);
1240 static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
1242 /* Renders columns C0...C1, plus headers, of rows R0...R1,
1243 at the given vertical position Y.
1244 C0 and C1 count vertical rules as columns,
1245 but R0 and R1 do not count horizontal rules as rows.
1246 Returns the vertical position after rendering. */
1248 render_rows (int y, int c0, int c1, int r0, int r1)
1251 for (r = r0; r < r1; r++)
1254 x = render_strip (x, y, r, 0, t->l * 2 + 1, r0, r1);
1255 x = render_strip (x, y, r, c0 * 2 + 1, c1 * 2, r0, r1);
1256 x = render_strip (x, y, r, (t->nc - t->r) * 2, t->nc * 2 + 1, r0, r1);
1257 y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2];
1262 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1263 current position on the current output device. */
1265 tabi_render (int c0, int r0, int c1, int r1)
1272 if (!(t->flags & SOMF_NO_TITLE))
1273 y += d->font_height;
1275 y = render_rows (y, c0, c1, 0, t->t * 2 + 1);
1276 y = render_rows (y, c0, c1, r0 * 2 + 1, r1 * 2);
1277 y = render_rows (y, c0, c1, (t->nr - t->b) * 2, t->nr * 2 + 1);
1280 const struct som_table_class tab_table_class =
1306 static enum outp_justification
1307 translate_justification (unsigned int opt)
1309 switch (opt & TAB_ALIGN_MASK)
1322 /* Returns the line style to use for drawing a rule of the given
1324 static enum outp_line_style
1325 rule_to_draw_type (unsigned char type)
1333 return OUTP_L_SINGLE;
1335 return OUTP_L_DOUBLE;
1341 /* Returns the horizontal rule at the given column and row. */
1343 get_hrule (int c, int r)
1345 return t->rh[c + r * t->cf];
1348 /* Returns the vertical rule at the given column and row. */
1350 get_vrule (int c, int r)
1352 return t->rv[c + r * (t->cf + 1)];
1355 /* Renders the horizontal rule at the given column and row
1356 at (X,Y) on the page. */
1358 render_horz_rule (int x, int y, int c, int r)
1360 enum outp_line_style style = rule_to_draw_type (get_hrule (c, r));
1361 if (style != OUTP_L_NONE)
1362 d->class->line (d, x, y, x + t->w[c], y + t->hrh[r],
1363 OUTP_L_NONE, style, OUTP_L_NONE, style);
1366 /* Renders the vertical rule at the given column and row
1367 at (X,Y) on the page. */
1369 render_vert_rule (int x, int y, int c, int r)
1371 enum outp_line_style style = rule_to_draw_type (get_vrule (c, r));
1372 if (style != OUTP_L_NONE)
1373 d->class->line (d, x, y, x + t->wrv[c], y + t->h[r],
1374 style, OUTP_L_NONE, style, OUTP_L_NONE);
1377 /* Renders the rule intersection at the given column and row
1378 at (X,Y) on the page. */
1380 render_rule_intersection (int x, int y, int c, int r)
1382 /* Bounds of intersection. */
1385 int x1 = x + t->wrv[c];
1386 int y1 = y + t->hrh[r];
1388 /* Lines on each side of intersection. */
1389 int top = r > 0 ? get_vrule (c, r - 1) : TAL_0;
1390 int left = c > 0 ? get_hrule (c - 1, r) : TAL_0;
1391 int bottom = r < t->nr ? get_vrule (c, r) : TAL_0;
1392 int right = c < t->nc ? get_hrule (c, r) : TAL_0;
1394 /* Output style for each line. */
1395 enum outp_line_style o_top = rule_to_draw_type (top);
1396 enum outp_line_style o_left = rule_to_draw_type (left);
1397 enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1398 enum outp_line_style o_right = rule_to_draw_type (right);
1400 if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1401 || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1402 d->class->line (d, x0, y0, x1, y1, o_top, o_left, o_bottom, o_right);
1405 /* Returns the width of columns C1...C2 exclusive,
1406 including interior but not exterior rules. */
1408 strip_width (int c1, int c2)
1413 for (c = c1; c < c2; c++)
1414 width += t->w[c] + t->wrv[c + 1];
1416 width -= t->wrv[c2];
1420 /* Returns the height of rows R1...R2 exclusive,
1421 including interior but not exterior rules. */
1423 strip_height (int r1, int r2)
1428 for (r = r1; r < r2; r++)
1429 height += t->h[r] + t->hrh[r + 1];
1431 height -= t->hrh[r2];
1435 /* Renders the cell at the given column and row at (X,Y) on the
1436 page. Also renders joined cells that extend as far to the
1437 right as C1 and as far down as R1. */
1439 render_cell (int x, int y, int c, int r, int c1, int r1)
1441 const int index = c + (r * t->cf);
1442 unsigned char type = t->ct[index];
1443 struct substring *content = &t->cc[index];
1445 if (!(type & TAB_JOIN))
1447 if (!(type & TAB_EMPTY))
1449 struct outp_text text;
1450 text.font = options_to_font (type);
1451 text.justification = translate_justification (type);
1452 text.string = *content;
1457 d->class->text_draw (d, &text);
1462 struct tab_joined_cell *j
1463 = (struct tab_joined_cell *) ss_data (*content);
1465 if (j->hit != tab_hit)
1469 if (j->x1 == c && j->y1 == r)
1471 struct outp_text text;
1472 text.font = options_to_font (type);
1473 text.justification = translate_justification (type);
1474 text.string = j->contents;
1477 text.h = strip_width (j->x1, MIN (j->x2, c1));
1478 text.v = strip_height (j->y1, MIN (j->y2, r1));
1479 d->class->text_draw (d, &text);
1485 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1486 on row R, at (X,Y). Returns X position after rendering.
1487 Also renders joined cells that extend beyond that strip,
1488 cropping them to lie within rendering region (C0,R0)-(C1,R1).
1489 C0 and C1 count vertical rules as columns.
1490 R counts horizontal rules as rows, but R0 and R1 do not. */
1492 render_strip (int x, int y, int r, int c0, int c1, int r0 UNUSED, int r1)
1496 for (c = c0; c < c1; c++)
1500 render_cell (x, y, c / 2, r / 2, c1 / 2, r1);
1502 render_horz_rule (x, y, c / 2, r / 2);
1508 render_vert_rule (x, y, c / 2, r / 2);
1510 render_rule_intersection (x, y, c / 2, r / 2);
1517 /* Sets COMMAND_NAME as the name of the current command,
1518 for embedding in output. */
1520 tab_set_command_name (const char *command_name_)
1522 free (command_name);
1523 command_name = command_name_ ? xstrdup (command_name_) : NULL;