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 <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 /* Set the title of table T to TITLE, which is formatted as if
376 passed to printf(). */
378 tab_title (struct tab_table *t, const char *title, ...)
382 assert (t != NULL && title != NULL);
383 va_start (args, title);
384 t->title = xvasprintf (title, args);
388 /* Set DIM_FUNC as the dimension function for table T. */
390 tab_dim (struct tab_table *t, tab_dim_func *dim_func, void *aux)
392 assert (t != NULL && t->dim == NULL);
397 /* Returns the natural width of column C in table T for driver D, that
398 is, the smallest width necessary to display all its cells without
399 wrapping. The width will be no larger than the page width minus
400 left and right rule widths. */
402 tab_natural_width (struct tab_table *t, struct outp_driver *d, int c)
406 assert (t != NULL && c >= 0 && c < t->nc);
410 for (width = r = 0; r < t->nr; r++)
412 struct outp_text text;
413 unsigned char opt = t->ct[c + r * t->cf];
416 if (opt & (TAB_JOIN | TAB_EMPTY))
419 text.string = t->cc[c + r * t->cf];
420 text.justification = OUTP_LEFT;
421 text.font = options_to_font (opt);
422 text.h = text.v = INT_MAX;
424 d->class->text_metrics (d, &text, &w, NULL);
432 /* FIXME: This is an ugly kluge to compensate for the fact
433 that we don't let joined cells contribute to column
435 width = d->prop_em_width * 8;
439 const int clamp = d->width - t->wrv[0] - t->wrv[t->nc];
448 /* Returns the natural height of row R in table T for driver D, that
449 is, the minimum height necessary to display the information in the
450 cell at the widths set for each column. */
452 tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
456 assert (t != NULL && r >= 0 && r < t->nr);
461 for (height = d->font_height, c = 0; c < t->nc; c++)
463 struct outp_text text;
464 unsigned char opt = t->ct[c + r * t->cf];
467 if (opt & (TAB_JOIN | TAB_EMPTY))
470 text.string = t->cc[c + r * t->cf];
471 text.justification = OUTP_LEFT;
472 text.font = options_to_font (opt);
475 d->class->text_metrics (d, &text, NULL, &h);
485 /* Callback function to set all columns and rows to their natural
486 dimensions. Not really meant to be called directly. */
488 tab_natural_dimensions (struct tab_table *t, struct outp_driver *d,
495 for (i = 0; i < t->nc; i++)
496 t->w[i] = tab_natural_width (t, d, i);
498 for (i = 0; i < t->nr; i++)
499 t->h[i] = tab_natural_height (t, d, i);
505 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
506 from V, displayed with format spec F. */
508 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
509 const union value *v, const struct fmt_spec *f)
513 assert (table != NULL && v != NULL && f != NULL);
515 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
516 || c + table->col_ofs >= table->nc
517 || r + table->row_ofs >= table->nr)
519 printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
521 c, table->col_ofs, c + table->col_ofs,
522 r, table->row_ofs, r + table->row_ofs,
523 table->nc, table->nr);
528 contents = pool_alloc (table->container, f->w);
529 table->cc[c + r * table->cf] = ss_buffer (contents, f->w);
530 table->ct[c + r * table->cf] = opt;
532 data_out (v, f, contents);
535 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
536 with NDEC decimal places. */
538 tab_fixed (struct tab_table *table, int c, int r, unsigned char opt,
539 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 data_out (&double_value, &f, buf);
574 while (isspace ((unsigned char) *cp) && cp < &buf[w])
576 f.w = w - (cp - buf);
578 contents = pool_alloc (table->container, f.w);
579 table->cc[c + r * table->cf] = ss_buffer (contents, f.w);
580 table->ct[c + r * table->cf] = opt;
581 memcpy (contents, cp, f.w);
584 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL as
586 If FMT is null, then the default print format will be used.
589 tab_double (struct tab_table *table, int c, int r, unsigned char opt,
590 double val, const struct fmt_spec *fmt)
596 union value double_value;
598 assert (table != NULL);
601 assert (c < table->nc);
603 assert (r < table->nr);
606 fmt = settings_get_format ();
608 fmt_check_output (fmt);
611 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
612 || c + table->col_ofs >= table->nc
613 || r + table->row_ofs >= table->nr)
615 printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
617 c, table->col_ofs, c + table->col_ofs,
618 r, table->row_ofs, r + table->row_ofs,
619 table->nc, table->nr);
624 double_value.f = val;
625 data_out (&double_value, fmt, buf);
628 while (isspace ((unsigned char) *cp) && cp < &buf[fmt->w])
630 w = fmt->w - (cp - buf);
632 contents = pool_alloc (table->container, w);
633 table->cc[c + r * table->cf] = ss_buffer (contents, w);
634 table->ct[c + r * table->cf] = opt;
635 memcpy (contents, cp, w);
640 do_tab_text (struct tab_table *table, int c, int r, unsigned opt, char *text)
644 assert (c < table->nc);
645 assert (r < table->nr);
648 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
649 || c + table->col_ofs >= table->nc
650 || r + table->row_ofs >= table->nr)
652 printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
654 c, table->col_ofs, c + table->col_ofs,
655 r, table->row_ofs, r + table->row_ofs,
656 table->nc, table->nr);
661 table->cc[c + r * table->cf] = ss_cstr (text);
662 table->ct[c + r * table->cf] = opt;
665 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
668 tab_text (struct tab_table *table, int c, int r, unsigned opt,
671 do_tab_text (table, c, r, opt, pool_strdup (table->container, text));
674 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
675 FORMAT, which is formatted as if passed to printf. */
677 tab_text_format (struct tab_table *table, int c, int r, unsigned opt,
678 const char *format, ...)
682 va_start (args, format);
683 do_tab_text (table, c, r, opt,
684 pool_vasprintf (table->container, format, args));
689 do_tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
690 unsigned opt, char *text)
692 struct tab_joined_cell *j;
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;
726 j->contents = ss_cstr (text);
731 struct substring *cc = &table->cc[x1 + y1 * table->cf];
732 unsigned char *ct = &table->ct[x1 + y1 * table->cf];
733 const int ofs = table->cf - (x2 - x1);
737 for (y = y1; y < y2; y++)
741 for (x = x1; x < x2; x++)
743 *cc++ = ss_buffer ((char *) j, 0);
753 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
754 options OPT to have text value TEXT. */
756 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
757 unsigned opt, const char *text)
759 do_tab_joint_text (table, x1, y1, x2, y2, opt,
760 pool_strdup (table->container, text));
763 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them
764 with options OPT to have text value FORMAT, which is formatted
765 as if passed to printf. */
767 tab_joint_text_format (struct tab_table *table, int x1, int y1, int x2, int y2,
768 unsigned opt, const char *format, ...)
772 va_start (args, format);
773 do_tab_joint_text (table, x1, y1, x2, y2, opt,
774 pool_vasprintf (table->container, format, args));
778 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
780 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
781 struct substring *string)
783 assert (table != NULL && string != NULL);
786 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
787 || c + table->col_ofs >= table->nc
788 || r + table->row_ofs >= table->nr)
790 printf ("tab_raw(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
792 c, table->col_ofs, c + table->col_ofs,
793 r, table->row_ofs, r + table->row_ofs,
794 table->nc, table->nr);
799 table->cc[c + r * table->cf] = *string;
800 table->ct[c + r * table->cf] = opt;
805 /* Sets the widths of all the columns and heights of all the rows in
806 table T for driver D. */
808 nowrap_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
810 t->w[0] = tab_natural_width (t, d, 0);
811 t->h[0] = d->font_height;
814 /* Sets the widths of all the columns and heights of all the rows in
815 table T for driver D. */
817 wrap_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
819 t->w[0] = tab_natural_width (t, d, 0);
820 t->h[0] = tab_natural_height (t, d, 0);
824 do_tab_output_text (struct tab_table *t, int options, char *text)
826 do_tab_text (t, 0, 0, options, text);
827 tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
828 tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL);
832 /* Outputs TEXT as a table with a single cell having cell options
833 OPTIONS, which is a combination of the TAB_* and TAT_*
836 tab_output_text (int options, const char *text)
838 struct tab_table *table = tab_create (1, 1, 0);
839 do_tab_output_text (table, options, pool_strdup (table->container, text));
842 /* Outputs FORMAT as a table with a single cell having cell
843 options OPTIONS, which is a combination of the TAB_* and TAT_*
844 constants. FORMAT is formatted as if it was passed through
847 tab_output_text_format (int options, const char *format, ...)
849 struct tab_table *table;
852 table = tab_create (1, 1, 0);
854 va_start (args, format);
855 do_tab_output_text (table, options,
856 pool_vasprintf (table->container, format, args));
860 /* Set table flags to FLAGS. */
862 tab_flags (struct tab_table *t, unsigned flags)
868 /* Easy, type-safe way to submit a tab table to som. */
870 tab_submit (struct tab_table *t)
875 s.class = &tab_table_class;
884 /* Set table row and column offsets for all functions that affect
887 tab_offset (struct tab_table *t, int col, int row)
893 if (row < -1 || row > t->nr)
895 printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
898 if (col < -1 || col > t->nc)
900 printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
906 diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
908 diff += (col - t->col_ofs), t->col_ofs = col;
914 /* Increment the row offset by one. If the table is too small,
915 increase its size. */
917 tab_next_row (struct tab_table *t)
922 if (++t->row_ofs >= t->nr)
923 tab_realloc (t, -1, t->nr * 4 / 3);
926 static struct tab_table *t;
927 static struct outp_driver *d;
930 /* Set the current table to TABLE. */
932 tabi_table (struct som_entity *table)
934 assert (table != NULL);
935 assert (table->type == SOM_TABLE);
938 tab_offset (t, 0, 0);
940 assert (t->w == NULL && t->h == NULL);
941 t->w = pool_nalloc (t->container, t->nc, sizeof *t->w);
942 t->h = pool_nalloc (t->container, t->nr, sizeof *t->h);
943 t->hrh = pool_nmalloc (t->container, t->nr + 1, sizeof *t->hrh);
944 t->wrv = pool_nmalloc (t->container, t->nc + 1, sizeof *t->wrv);
947 /* Returns the line style to use for spacing purposes for a rule
948 of the given TYPE. */
949 static enum outp_line_style
950 rule_to_spacing_type (unsigned char type)
958 return OUTP_L_SINGLE;
960 return OUTP_L_DOUBLE;
966 /* Set the current output device to DRIVER. */
968 tabi_driver (struct outp_driver *driver)
973 assert (driver != NULL);
976 /* Figure out sizes of rules. */
977 for (r = 0; r <= t->nr; r++)
980 for (c = 0; c < t->nc; c++)
982 unsigned char rh = t->rh[c + r * t->cf];
983 int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
990 for (c = 0; c <= t->nc; c++)
993 for (r = 0; r < t->nr; r++)
995 unsigned char *rv = &t->rv[c + r * (t->cf + 1)];
997 if (*rv == UCHAR_MAX)
998 *rv = c != 0 && c != t->nc ? TAL_GAP : TAL_0;
999 w = driver->vert_line_width[rule_to_spacing_type (*rv)];
1007 for (i = 0; i < t->nr; i++)
1009 for (i = 0; i < t->nc; i++)
1013 assert (t->dim != NULL);
1014 t->dim (t, d, t->dim_aux);
1020 for (i = 0; i < t->nr; i++)
1024 printf ("Table row %d height not initialized.\n", i);
1027 assert (t->h[i] > 0);
1030 for (i = 0; i < t->nc; i++)
1034 printf ("Table column %d width not initialized.\n", i);
1037 assert (t->w[i] > 0);
1042 /* Add up header sizes. */
1043 for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
1044 t->wl += t->w[i] + t->wrv[i + 1];
1045 for (i = 0, t->ht = t->hrh[0]; i < t->t; i++)
1046 t->ht += t->h[i] + t->hrh[i + 1];
1047 for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++)
1048 t->wr += t->w[i] + t->wrv[i + 1];
1049 for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
1050 t->hb += t->h[i] + t->hrh[i + 1];
1053 if (!(t->flags & SOMF_NO_TITLE))
1054 t->ht += d->font_height;
1057 /* Return the number of columns and rows in the table into N_COLUMNS
1058 and N_ROWS, respectively. */
1060 tabi_count (int *n_columns, int *n_rows)
1062 assert (n_columns != NULL && n_rows != NULL);
1067 static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual);
1069 /* Return the horizontal and vertical size of the entire table,
1070 including headers, for the current output device, into HORIZ and
1073 tabi_area (int *horiz, int *vert)
1075 assert (horiz != NULL && vert != NULL);
1080 for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
1081 c < t->nc - t->r; c++)
1082 w += t->w[c] + t->wrv[c];
1088 for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
1089 r < t->nr - t->b; r++)
1090 h += t->h[r] + t->hrh[r];
1095 /* Return the column style for this table into STYLE. */
1097 tabi_columns (int *style)
1099 assert (style != NULL);
1100 *style = t->col_style;
1103 /* Return the number of header rows/columns on the left, right, top,
1104 and bottom sides into HL, HR, HT, and HB, respectively. */
1106 tabi_headers (int *hl, int *hr, int *ht, int *hb)
1108 assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL);
1115 /* Determines the number of rows or columns (including appropriate
1116 headers), depending on CUMTYPE, that will fit into the space
1117 specified. Takes rows/columns starting at index START and attempts
1118 to fill up available space MAX. Returns in END the index of the
1119 last row/column plus one; returns in ACTUAL the actual amount of
1120 space the selected rows/columns (including appropriate headers)
1123 tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
1130 assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1131 if (cumtype == SOM_ROWS)
1133 assert (start >= 0 && start < t->nr);
1136 r = &t->hrh[start + 1];
1137 total = t->ht + t->hb;
1141 assert (start >= 0 && start < t->nc);
1144 r = &t->wrv[start + 1];
1145 total = t->wl + t->wr;
1161 for (x = start + 1; x < n; x++)
1163 int amt = *d++ + *r++;
1181 /* Return flags set for the current table into FLAGS. */
1183 tabi_flags (unsigned *flags)
1185 assert (flags != NULL);
1189 /* Returns true if the table will fit in the given page WIDTH,
1192 tabi_fits_width (int width)
1196 for (i = t->l; i < t->nc - t->r; i++)
1197 if (t->wl + t->wr + t->w[i] > width)
1203 /* Returns true if the table will fit in the given page LENGTH,
1206 tabi_fits_length (int length)
1210 for (i = t->t; i < t->nr - t->b; i++)
1211 if (t->ht + t->hb + t->h[i] > length)
1217 /* Sets the number of header rows/columns on the left, right, top,
1218 and bottom sides to HL, HR, HT, and HB, respectively. */
1220 tabi_set_headers (int hl, int hr, int ht, int hb)
1228 /* Render title for current table, with major index X and minor index
1229 Y. Y may be zero, or X and Y may be zero, but X should be nonzero
1232 tabi_title (int x, int y)
1237 if (t->flags & SOMF_NO_TITLE)
1240 cp = spprintf (buf, "%d.%d", table_num, subtable_num);
1242 cp = spprintf (cp, "(%d:%d)", x, y);
1244 cp = spprintf (cp, "(%d)", x);
1245 if (command_name != NULL)
1246 cp = spprintf (cp, " %s", command_name);
1247 cp = stpcpy (cp, ". ");
1248 if (t->title != NULL)
1250 size_t length = strlen (t->title);
1251 memcpy (cp, t->title, length);
1257 struct outp_text text;
1259 text.font = OUTP_PROPORTIONAL;
1260 text.justification = OUTP_LEFT;
1261 text.string = ss_buffer (buf, cp - buf);
1263 text.v = d->font_height;
1266 d->class->text_draw (d, &text);
1270 static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
1272 /* Renders columns C0...C1, plus headers, of rows R0...R1,
1273 at the given vertical position Y.
1274 C0 and C1 count vertical rules as columns,
1275 but R0 and R1 do not count horizontal rules as rows.
1276 Returns the vertical position after rendering. */
1278 render_rows (int y, int c0, int c1, int r0, int r1)
1281 for (r = r0; r < r1; r++)
1284 x = render_strip (x, y, r, 0, t->l * 2 + 1, r0, r1);
1285 x = render_strip (x, y, r, c0 * 2 + 1, c1 * 2, r0, r1);
1286 x = render_strip (x, y, r, (t->nc - t->r) * 2, t->nc * 2 + 1, r0, r1);
1287 y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2];
1292 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1293 current position on the current output device. */
1295 tabi_render (int c0, int r0, int c1, int r1)
1302 if (!(t->flags & SOMF_NO_TITLE))
1303 y += d->font_height;
1305 y = render_rows (y, c0, c1, 0, t->t * 2 + 1);
1306 y = render_rows (y, c0, c1, r0 * 2 + 1, r1 * 2);
1307 y = render_rows (y, c0, c1, (t->nr - t->b) * 2, t->nr * 2 + 1);
1310 const struct som_table_class tab_table_class =
1336 static enum outp_justification
1337 translate_justification (unsigned int opt)
1339 switch (opt & TAB_ALIGN_MASK)
1352 /* Returns the line style to use for drawing a rule of the given
1354 static enum outp_line_style
1355 rule_to_draw_type (unsigned char type)
1363 return OUTP_L_SINGLE;
1365 return OUTP_L_DOUBLE;
1371 /* Returns the horizontal rule at the given column and row. */
1373 get_hrule (int c, int r)
1375 return t->rh[c + r * t->cf];
1378 /* Returns the vertical rule at the given column and row. */
1380 get_vrule (int c, int r)
1382 return t->rv[c + r * (t->cf + 1)];
1385 /* Renders the horizontal rule at the given column and row
1386 at (X,Y) on the page. */
1388 render_horz_rule (int x, int y, int c, int r)
1390 enum outp_line_style style = rule_to_draw_type (get_hrule (c, r));
1391 if (style != OUTP_L_NONE)
1392 d->class->line (d, x, y, x + t->w[c], y + t->hrh[r],
1393 OUTP_L_NONE, style, OUTP_L_NONE, style);
1396 /* Renders the vertical rule at the given column and row
1397 at (X,Y) on the page. */
1399 render_vert_rule (int x, int y, int c, int r)
1401 enum outp_line_style style = rule_to_draw_type (get_vrule (c, r));
1402 if (style != OUTP_L_NONE)
1403 d->class->line (d, x, y, x + t->wrv[c], y + t->h[r],
1404 style, OUTP_L_NONE, style, OUTP_L_NONE);
1407 /* Renders the rule intersection at the given column and row
1408 at (X,Y) on the page. */
1410 render_rule_intersection (int x, int y, int c, int r)
1412 /* Bounds of intersection. */
1415 int x1 = x + t->wrv[c];
1416 int y1 = y + t->hrh[r];
1418 /* Lines on each side of intersection. */
1419 int top = r > 0 ? get_vrule (c, r - 1) : TAL_0;
1420 int left = c > 0 ? get_hrule (c - 1, r) : TAL_0;
1421 int bottom = r < t->nr ? get_vrule (c, r) : TAL_0;
1422 int right = c < t->nc ? get_hrule (c, r) : TAL_0;
1424 /* Output style for each line. */
1425 enum outp_line_style o_top = rule_to_draw_type (top);
1426 enum outp_line_style o_left = rule_to_draw_type (left);
1427 enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1428 enum outp_line_style o_right = rule_to_draw_type (right);
1430 if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1431 || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1432 d->class->line (d, x0, y0, x1, y1, o_top, o_left, o_bottom, o_right);
1435 /* Returns the width of columns C1...C2 exclusive,
1436 including interior but not exterior rules. */
1438 strip_width (int c1, int c2)
1443 for (c = c1; c < c2; c++)
1444 width += t->w[c] + t->wrv[c + 1];
1446 width -= t->wrv[c2];
1450 /* Returns the height of rows R1...R2 exclusive,
1451 including interior but not exterior rules. */
1453 strip_height (int r1, int r2)
1458 for (r = r1; r < r2; r++)
1459 height += t->h[r] + t->hrh[r + 1];
1461 height -= t->hrh[r2];
1465 /* Renders the cell at the given column and row at (X,Y) on the
1466 page. Also renders joined cells that extend as far to the
1467 right as C1 and as far down as R1. */
1469 render_cell (int x, int y, int c, int r, int c1, int r1)
1471 const int index = c + (r * t->cf);
1472 unsigned char type = t->ct[index];
1473 struct substring *content = &t->cc[index];
1475 if (!(type & TAB_JOIN))
1477 if (!(type & TAB_EMPTY))
1479 struct outp_text text;
1480 text.font = options_to_font (type);
1481 text.justification = translate_justification (type);
1482 text.string = *content;
1487 d->class->text_draw (d, &text);
1492 struct tab_joined_cell *j
1493 = (struct tab_joined_cell *) ss_data (*content);
1495 if (j->hit != tab_hit)
1499 if (j->x1 == c && j->y1 == r)
1501 struct outp_text text;
1502 text.font = options_to_font (type);
1503 text.justification = translate_justification (type);
1504 text.string = j->contents;
1507 text.h = strip_width (j->x1, MIN (j->x2, c1));
1508 text.v = strip_height (j->y1, MIN (j->y2, r1));
1509 d->class->text_draw (d, &text);
1515 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1516 on row R, at (X,Y). Returns X position after rendering.
1517 Also renders joined cells that extend beyond that strip,
1518 cropping them to lie within rendering region (C0,R0)-(C1,R1).
1519 C0 and C1 count vertical rules as columns.
1520 R counts horizontal rules as rows, but R0 and R1 do not. */
1522 render_strip (int x, int y, int r, int c0, int c1, int r0 UNUSED, int r1)
1526 for (c = c0; c < c1; c++)
1530 render_cell (x, y, c / 2, r / 2, c1 / 2, r1);
1532 render_horz_rule (x, y, c / 2, r / 2);
1538 render_vert_rule (x, y, c / 2, r / 2);
1540 render_rule_intersection (x, y, c / 2, r / 2);
1547 /* Sets COMMAND_NAME as the name of the current command,
1548 for embedding in output. */
1550 tab_set_command_name (const char *command_name_)
1552 free (command_name);
1553 command_name = command_name_ ? xstrdup (command_name_) : NULL;