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 /* Set the title of table T to TITLE, which is formatted as if
377 passed to printf(). */
379 tab_title (struct tab_table *t, const char *title, ...)
383 assert (t != NULL && title != NULL);
384 va_start (args, title);
385 t->title = xvasprintf (title, args);
389 /* Set DIM_FUNC as the dimension function for table T. */
391 tab_dim (struct tab_table *t, tab_dim_func *dim_func, void *aux)
393 assert (t != NULL && t->dim == NULL);
398 /* Returns the natural width of column C in table T for driver D, that
399 is, the smallest width necessary to display all its cells without
400 wrapping. The width will be no larger than the page width minus
401 left and right rule widths. */
403 tab_natural_width (struct tab_table *t, struct outp_driver *d, int c)
407 assert (t != NULL && c >= 0 && c < t->nc);
411 for (width = r = 0; r < t->nr; r++)
413 struct outp_text text;
414 unsigned char opt = t->ct[c + r * t->cf];
417 if (opt & (TAB_JOIN | TAB_EMPTY))
420 text.string = t->cc[c + r * t->cf];
421 text.justification = OUTP_LEFT;
422 text.font = options_to_font (opt);
423 text.h = text.v = INT_MAX;
425 d->class->text_metrics (d, &text, &w, NULL);
433 /* FIXME: This is an ugly kluge to compensate for the fact
434 that we don't let joined cells contribute to column
436 width = d->prop_em_width * 8;
440 const int clamp = d->width - t->wrv[0] - t->wrv[t->nc];
449 /* Returns the natural height of row R in table T for driver D, that
450 is, the minimum height necessary to display the information in the
451 cell at the widths set for each column. */
453 tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
457 assert (t != NULL && r >= 0 && r < t->nr);
462 for (height = d->font_height, c = 0; c < t->nc; c++)
464 struct outp_text text;
465 unsigned char opt = t->ct[c + r * t->cf];
468 if (opt & (TAB_JOIN | TAB_EMPTY))
471 text.string = t->cc[c + r * t->cf];
472 text.justification = OUTP_LEFT;
473 text.font = options_to_font (opt);
476 d->class->text_metrics (d, &text, NULL, &h);
486 /* Callback function to set all columns and rows to their natural
487 dimensions. Not really meant to be called directly. */
489 tab_natural_dimensions (struct tab_table *t, struct outp_driver *d,
496 for (i = 0; i < t->nc; i++)
497 t->w[i] = tab_natural_width (t, d, i);
499 for (i = 0; i < t->nr; i++)
500 t->h[i] = tab_natural_height (t, d, i);
506 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
507 from V, displayed with format spec F. */
509 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
510 const union value *v, const struct dictionary *dict,
511 const struct fmt_spec *f)
515 assert (table != NULL && v != NULL && f != NULL);
517 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
518 || c + table->col_ofs >= table->nc
519 || r + table->row_ofs >= table->nr)
521 printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
523 c, table->col_ofs, c + table->col_ofs,
524 r, table->row_ofs, r + table->row_ofs,
525 table->nc, table->nr);
530 contents = data_out_pool (v, dict_get_encoding (dict), f, table->container);
532 table->cc[c + r * table->cf] = ss_cstr (contents);
533 table->ct[c + r * table->cf] = opt;
536 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
537 with NDEC decimal places. */
539 tab_fixed (struct tab_table *table, int c, int r, unsigned char opt,
540 double val, int w, int d)
545 union value double_value;
547 assert (table != NULL && w <= 40);
550 assert (c < table->nc);
552 assert (r < table->nr);
554 f = fmt_for_output (FMT_F, w, d);
557 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
558 || c + table->col_ofs >= table->nc
559 || r + table->row_ofs >= table->nr)
561 printf ("tab_fixed(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
563 c, table->col_ofs, c + table->col_ofs,
564 r, table->row_ofs, r + table->row_ofs,
565 table->nc, table->nr);
570 double_value.f = val;
571 s = data_out_pool (&double_value, LEGACY_NATIVE, &f, table->container);
574 while (isspace ((unsigned char) *cp) && cp < &s[w])
578 table->cc[c + r * table->cf] = ss_buffer (cp, f.w);
579 table->ct[c + r * table->cf] = opt;
582 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL as
584 If FMT is null, then the default print format will be used.
587 tab_double (struct tab_table *table, int c, int r, unsigned char opt,
588 double val, const struct fmt_spec *fmt)
591 union value double_value ;
593 assert (table != NULL);
596 assert (c < table->nc);
598 assert (r < table->nr);
601 fmt = settings_get_format ();
603 fmt_check_output (fmt);
606 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
607 || c + table->col_ofs >= table->nc
608 || r + table->row_ofs >= table->nr)
610 printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
612 c, table->col_ofs, c + table->col_ofs,
613 r, table->row_ofs, r + table->row_ofs,
614 table->nc, table->nr);
619 double_value.f = val;
620 ss = ss_cstr (data_out_pool (&double_value, LEGACY_NATIVE, fmt, table->container));
622 ss_ltrim (&ss, ss_cstr (" "));
624 table->cc[c + r * table->cf] = ss;
625 table->ct[c + r * table->cf] = opt;
630 do_tab_text (struct tab_table *table, int c, int r, unsigned opt, char *text)
634 assert (c < table->nc);
635 assert (r < table->nr);
638 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
639 || c + table->col_ofs >= table->nc
640 || r + table->row_ofs >= table->nr)
642 printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
644 c, table->col_ofs, c + table->col_ofs,
645 r, table->row_ofs, r + table->row_ofs,
646 table->nc, table->nr);
651 table->cc[c + r * table->cf] = ss_cstr (text);
652 table->ct[c + r * table->cf] = opt;
655 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
658 tab_text (struct tab_table *table, int c, int r, unsigned opt,
661 do_tab_text (table, c, r, opt, pool_strdup (table->container, text));
664 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
665 FORMAT, which is formatted as if passed to printf. */
667 tab_text_format (struct tab_table *table, int c, int r, unsigned opt,
668 const char *format, ...)
672 va_start (args, format);
673 do_tab_text (table, c, r, opt,
674 pool_vasprintf (table->container, format, args));
679 do_tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
680 unsigned opt, char *text)
682 struct tab_joined_cell *j;
684 assert (x1 + table->col_ofs >= 0);
685 assert (y1 + table->row_ofs >= 0);
688 assert (y2 + table->row_ofs < table->nr);
689 assert (x2 + table->col_ofs < table->nc);
692 if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
693 || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
694 || x2 < x1 || x2 + table->col_ofs >= table->nc
695 || y2 < y2 || y2 + table->row_ofs >= table->nr)
697 printf ("tab_joint_text(): bad cell "
698 "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
699 x1, table->col_ofs, x1 + table->col_ofs,
700 y1, table->row_ofs, y1 + table->row_ofs,
701 x2, table->col_ofs, x2 + table->col_ofs,
702 y2, table->row_ofs, y2 + table->row_ofs,
703 table->nc, table->nr);
708 tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
710 j = pool_alloc (table->container, sizeof *j);
712 j->x1 = x1 + table->col_ofs;
713 j->y1 = y1 + table->row_ofs;
714 j->x2 = ++x2 + table->col_ofs;
715 j->y2 = ++y2 + table->row_ofs;
716 j->contents = ss_cstr (text);
721 struct substring *cc = &table->cc[x1 + y1 * table->cf];
722 unsigned char *ct = &table->ct[x1 + y1 * table->cf];
723 const int ofs = table->cf - (x2 - x1);
727 for (y = y1; y < y2; y++)
731 for (x = x1; x < x2; x++)
733 *cc++ = ss_buffer ((char *) j, 0);
743 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
744 options OPT to have text value TEXT. */
746 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
747 unsigned opt, const char *text)
749 do_tab_joint_text (table, x1, y1, x2, y2, opt,
750 pool_strdup (table->container, text));
753 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them
754 with options OPT to have text value FORMAT, which is formatted
755 as if passed to printf. */
757 tab_joint_text_format (struct tab_table *table, int x1, int y1, int x2, int y2,
758 unsigned opt, const char *format, ...)
762 va_start (args, format);
763 do_tab_joint_text (table, x1, y1, x2, y2, opt,
764 pool_vasprintf (table->container, format, args));
768 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
770 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
771 struct substring *string)
773 assert (table != NULL && string != NULL);
776 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
777 || c + table->col_ofs >= table->nc
778 || r + table->row_ofs >= table->nr)
780 printf ("tab_raw(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
782 c, table->col_ofs, c + table->col_ofs,
783 r, table->row_ofs, r + table->row_ofs,
784 table->nc, table->nr);
789 table->cc[c + r * table->cf] = *string;
790 table->ct[c + r * table->cf] = opt;
795 /* Sets the widths of all the columns and heights of all the rows in
796 table T for driver D. */
798 nowrap_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] = d->font_height;
804 /* Sets the widths of all the columns and heights of all the rows in
805 table T for driver D. */
807 wrap_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
809 t->w[0] = tab_natural_width (t, d, 0);
810 t->h[0] = tab_natural_height (t, d, 0);
814 do_tab_output_text (struct tab_table *t, int options, char *text)
816 do_tab_text (t, 0, 0, options, text);
817 tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
818 tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL);
822 /* Outputs TEXT as a table with a single cell having cell options
823 OPTIONS, which is a combination of the TAB_* and TAT_*
826 tab_output_text (int options, const char *text)
828 struct tab_table *table = tab_create (1, 1, 0);
829 do_tab_output_text (table, options, pool_strdup (table->container, text));
832 /* Outputs FORMAT as a table with a single cell having cell
833 options OPTIONS, which is a combination of the TAB_* and TAT_*
834 constants. FORMAT is formatted as if it was passed through
837 tab_output_text_format (int options, const char *format, ...)
839 struct tab_table *table;
842 table = tab_create (1, 1, 0);
844 va_start (args, format);
845 do_tab_output_text (table, options,
846 pool_vasprintf (table->container, format, args));
850 /* Set table flags to FLAGS. */
852 tab_flags (struct tab_table *t, unsigned flags)
858 /* Easy, type-safe way to submit a tab table to som. */
860 tab_submit (struct tab_table *t)
865 s.class = &tab_table_class;
874 /* Set table row and column offsets for all functions that affect
877 tab_offset (struct tab_table *t, int col, int row)
883 if (row < -1 || row > t->nr)
885 printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
888 if (col < -1 || col > t->nc)
890 printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
896 diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
898 diff += (col - t->col_ofs), t->col_ofs = col;
904 /* Increment the row offset by one. If the table is too small,
905 increase its size. */
907 tab_next_row (struct tab_table *t)
912 if (++t->row_ofs >= t->nr)
913 tab_realloc (t, -1, t->nr * 4 / 3);
916 static struct tab_table *t;
917 static struct outp_driver *d;
920 /* Set the current table to TABLE. */
922 tabi_table (struct som_entity *table)
924 assert (table != NULL);
925 assert (table->type == SOM_TABLE);
928 tab_offset (t, 0, 0);
930 assert (t->w == NULL && t->h == NULL);
931 t->w = pool_nalloc (t->container, t->nc, sizeof *t->w);
932 t->h = pool_nalloc (t->container, t->nr, sizeof *t->h);
933 t->hrh = pool_nmalloc (t->container, t->nr + 1, sizeof *t->hrh);
934 t->wrv = pool_nmalloc (t->container, t->nc + 1, sizeof *t->wrv);
937 /* Returns the line style to use for spacing purposes for a rule
938 of the given TYPE. */
939 static enum outp_line_style
940 rule_to_spacing_type (unsigned char type)
948 return OUTP_L_SINGLE;
950 return OUTP_L_DOUBLE;
956 /* Set the current output device to DRIVER. */
958 tabi_driver (struct outp_driver *driver)
963 assert (driver != NULL);
966 /* Figure out sizes of rules. */
967 for (r = 0; r <= t->nr; r++)
970 for (c = 0; c < t->nc; c++)
972 unsigned char rh = t->rh[c + r * t->cf];
973 int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
980 for (c = 0; c <= t->nc; c++)
983 for (r = 0; r < t->nr; r++)
985 unsigned char *rv = &t->rv[c + r * (t->cf + 1)];
987 if (*rv == UCHAR_MAX)
988 *rv = c != 0 && c != t->nc ? TAL_GAP : TAL_0;
989 w = driver->vert_line_width[rule_to_spacing_type (*rv)];
997 for (i = 0; i < t->nr; i++)
999 for (i = 0; i < t->nc; i++)
1003 assert (t->dim != NULL);
1004 t->dim (t, d, t->dim_aux);
1010 for (i = 0; i < t->nr; i++)
1014 printf ("Table row %d height not initialized.\n", i);
1017 assert (t->h[i] > 0);
1020 for (i = 0; i < t->nc; i++)
1024 printf ("Table column %d width not initialized.\n", i);
1027 assert (t->w[i] > 0);
1032 /* Add up header sizes. */
1033 for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
1034 t->wl += t->w[i] + t->wrv[i + 1];
1035 for (i = 0, t->ht = t->hrh[0]; i < t->t; i++)
1036 t->ht += t->h[i] + t->hrh[i + 1];
1037 for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++)
1038 t->wr += t->w[i] + t->wrv[i + 1];
1039 for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
1040 t->hb += t->h[i] + t->hrh[i + 1];
1043 if (!(t->flags & SOMF_NO_TITLE))
1044 t->ht += d->font_height;
1047 /* Return the number of columns and rows in the table into N_COLUMNS
1048 and N_ROWS, respectively. */
1050 tabi_count (int *n_columns, int *n_rows)
1052 assert (n_columns != NULL && n_rows != NULL);
1057 static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual);
1059 /* Return the horizontal and vertical size of the entire table,
1060 including headers, for the current output device, into HORIZ and
1063 tabi_area (int *horiz, int *vert)
1065 assert (horiz != NULL && vert != NULL);
1070 for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
1071 c < t->nc - t->r; c++)
1072 w += t->w[c] + t->wrv[c];
1078 for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
1079 r < t->nr - t->b; r++)
1080 h += t->h[r] + t->hrh[r];
1085 /* Return the column style for this table into STYLE. */
1087 tabi_columns (int *style)
1089 assert (style != NULL);
1090 *style = t->col_style;
1093 /* Return the number of header rows/columns on the left, right, top,
1094 and bottom sides into HL, HR, HT, and HB, respectively. */
1096 tabi_headers (int *hl, int *hr, int *ht, int *hb)
1098 assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL);
1105 /* Determines the number of rows or columns (including appropriate
1106 headers), depending on CUMTYPE, that will fit into the space
1107 specified. Takes rows/columns starting at index START and attempts
1108 to fill up available space MAX. Returns in END the index of the
1109 last row/column plus one; returns in ACTUAL the actual amount of
1110 space the selected rows/columns (including appropriate headers)
1113 tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
1120 assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1121 if (cumtype == SOM_ROWS)
1123 assert (start >= 0 && start < t->nr);
1126 r = &t->hrh[start + 1];
1127 total = t->ht + t->hb;
1131 assert (start >= 0 && start < t->nc);
1134 r = &t->wrv[start + 1];
1135 total = t->wl + t->wr;
1151 for (x = start + 1; x < n; x++)
1153 int amt = *d++ + *r++;
1171 /* Return flags set for the current table into FLAGS. */
1173 tabi_flags (unsigned *flags)
1175 assert (flags != NULL);
1179 /* Returns true if the table will fit in the given page WIDTH,
1182 tabi_fits_width (int width)
1186 for (i = t->l; i < t->nc - t->r; i++)
1187 if (t->wl + t->wr + t->w[i] > width)
1193 /* Returns true if the table will fit in the given page LENGTH,
1196 tabi_fits_length (int length)
1200 for (i = t->t; i < t->nr - t->b; i++)
1201 if (t->ht + t->hb + t->h[i] > length)
1207 /* Sets the number of header rows/columns on the left, right, top,
1208 and bottom sides to HL, HR, HT, and HB, respectively. */
1210 tabi_set_headers (int hl, int hr, int ht, int hb)
1218 /* Render title for current table, with major index X and minor index
1219 Y. Y may be zero, or X and Y may be zero, but X should be nonzero
1222 tabi_title (int x, int y)
1227 if (t->flags & SOMF_NO_TITLE)
1230 cp = spprintf (buf, "%d.%d", table_num, subtable_num);
1232 cp = spprintf (cp, "(%d:%d)", x, y);
1234 cp = spprintf (cp, "(%d)", x);
1235 if (command_name != NULL)
1236 cp = spprintf (cp, " %s", command_name);
1237 cp = stpcpy (cp, ". ");
1238 if (t->title != NULL)
1240 size_t length = strlen (t->title);
1241 memcpy (cp, t->title, length);
1247 struct outp_text text;
1249 text.font = OUTP_PROPORTIONAL;
1250 text.justification = OUTP_LEFT;
1251 text.string = ss_buffer (buf, cp - buf);
1253 text.v = d->font_height;
1256 d->class->text_draw (d, &text);
1260 static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
1262 /* Renders columns C0...C1, plus headers, of rows R0...R1,
1263 at the given vertical position Y.
1264 C0 and C1 count vertical rules as columns,
1265 but R0 and R1 do not count horizontal rules as rows.
1266 Returns the vertical position after rendering. */
1268 render_rows (int y, int c0, int c1, int r0, int r1)
1271 for (r = r0; r < r1; r++)
1274 x = render_strip (x, y, r, 0, t->l * 2 + 1, r0, r1);
1275 x = render_strip (x, y, r, c0 * 2 + 1, c1 * 2, r0, r1);
1276 x = render_strip (x, y, r, (t->nc - t->r) * 2, t->nc * 2 + 1, r0, r1);
1277 y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2];
1282 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1283 current position on the current output device. */
1285 tabi_render (int c0, int r0, int c1, int r1)
1292 if (!(t->flags & SOMF_NO_TITLE))
1293 y += d->font_height;
1295 y = render_rows (y, c0, c1, 0, t->t * 2 + 1);
1296 y = render_rows (y, c0, c1, r0 * 2 + 1, r1 * 2);
1297 y = render_rows (y, c0, c1, (t->nr - t->b) * 2, t->nr * 2 + 1);
1300 const struct som_table_class tab_table_class =
1326 static enum outp_justification
1327 translate_justification (unsigned int opt)
1329 switch (opt & TAB_ALIGN_MASK)
1342 /* Returns the line style to use for drawing a rule of the given
1344 static enum outp_line_style
1345 rule_to_draw_type (unsigned char type)
1353 return OUTP_L_SINGLE;
1355 return OUTP_L_DOUBLE;
1361 /* Returns the horizontal rule at the given column and row. */
1363 get_hrule (int c, int r)
1365 return t->rh[c + r * t->cf];
1368 /* Returns the vertical rule at the given column and row. */
1370 get_vrule (int c, int r)
1372 return t->rv[c + r * (t->cf + 1)];
1375 /* Renders the horizontal rule at the given column and row
1376 at (X,Y) on the page. */
1378 render_horz_rule (int x, int y, int c, int r)
1380 enum outp_line_style style = rule_to_draw_type (get_hrule (c, r));
1381 if (style != OUTP_L_NONE)
1382 d->class->line (d, x, y, x + t->w[c], y + t->hrh[r],
1383 OUTP_L_NONE, style, OUTP_L_NONE, style);
1386 /* Renders the vertical rule at the given column and row
1387 at (X,Y) on the page. */
1389 render_vert_rule (int x, int y, int c, int r)
1391 enum outp_line_style style = rule_to_draw_type (get_vrule (c, r));
1392 if (style != OUTP_L_NONE)
1393 d->class->line (d, x, y, x + t->wrv[c], y + t->h[r],
1394 style, OUTP_L_NONE, style, OUTP_L_NONE);
1397 /* Renders the rule intersection at the given column and row
1398 at (X,Y) on the page. */
1400 render_rule_intersection (int x, int y, int c, int r)
1402 /* Bounds of intersection. */
1405 int x1 = x + t->wrv[c];
1406 int y1 = y + t->hrh[r];
1408 /* Lines on each side of intersection. */
1409 int top = r > 0 ? get_vrule (c, r - 1) : TAL_0;
1410 int left = c > 0 ? get_hrule (c - 1, r) : TAL_0;
1411 int bottom = r < t->nr ? get_vrule (c, r) : TAL_0;
1412 int right = c < t->nc ? get_hrule (c, r) : TAL_0;
1414 /* Output style for each line. */
1415 enum outp_line_style o_top = rule_to_draw_type (top);
1416 enum outp_line_style o_left = rule_to_draw_type (left);
1417 enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1418 enum outp_line_style o_right = rule_to_draw_type (right);
1420 if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1421 || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1422 d->class->line (d, x0, y0, x1, y1, o_top, o_left, o_bottom, o_right);
1425 /* Returns the width of columns C1...C2 exclusive,
1426 including interior but not exterior rules. */
1428 strip_width (int c1, int c2)
1433 for (c = c1; c < c2; c++)
1434 width += t->w[c] + t->wrv[c + 1];
1436 width -= t->wrv[c2];
1440 /* Returns the height of rows R1...R2 exclusive,
1441 including interior but not exterior rules. */
1443 strip_height (int r1, int r2)
1448 for (r = r1; r < r2; r++)
1449 height += t->h[r] + t->hrh[r + 1];
1451 height -= t->hrh[r2];
1455 /* Renders the cell at the given column and row at (X,Y) on the
1456 page. Also renders joined cells that extend as far to the
1457 right as C1 and as far down as R1. */
1459 render_cell (int x, int y, int c, int r, int c1, int r1)
1461 const int index = c + (r * t->cf);
1462 unsigned char type = t->ct[index];
1463 struct substring *content = &t->cc[index];
1465 if (!(type & TAB_JOIN))
1467 if (!(type & TAB_EMPTY))
1469 struct outp_text text;
1470 text.font = options_to_font (type);
1471 text.justification = translate_justification (type);
1472 text.string = *content;
1477 d->class->text_draw (d, &text);
1482 struct tab_joined_cell *j
1483 = (struct tab_joined_cell *) ss_data (*content);
1485 if (j->hit != tab_hit)
1489 if (j->x1 == c && j->y1 == r)
1491 struct outp_text text;
1492 text.font = options_to_font (type);
1493 text.justification = translate_justification (type);
1494 text.string = j->contents;
1497 text.h = strip_width (j->x1, MIN (j->x2, c1));
1498 text.v = strip_height (j->y1, MIN (j->y2, r1));
1499 d->class->text_draw (d, &text);
1505 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1506 on row R, at (X,Y). Returns X position after rendering.
1507 Also renders joined cells that extend beyond that strip,
1508 cropping them to lie within rendering region (C0,R0)-(C1,R1).
1509 C0 and C1 count vertical rules as columns.
1510 R counts horizontal rules as rows, but R0 and R1 do not. */
1512 render_strip (int x, int y, int r, int c0, int c1, int r0 UNUSED, int r1)
1516 for (c = c0; c < c1; c++)
1520 render_cell (x, y, c / 2, r / 2, c1 / 2, r1);
1522 render_horz_rule (x, y, c / 2, r / 2);
1528 render_vert_rule (x, y, c / 2, r / 2);
1530 render_rule_intersection (x, y, c / 2, r / 2);
1537 /* Sets COMMAND_NAME as the name of the current command,
1538 for embedding in output. */
1540 tab_set_command_name (const char *command_name_)
1542 free (command_name);
1543 command_name = command_name_ ? xstrdup (command_name_) : NULL;