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 /* 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, void *aux)
404 assert (t != NULL && t->dim == NULL);
409 /* Returns the natural width of column C in table T for driver D, that
410 is, the smallest width necessary to display all its cells without
411 wrapping. The width will be no larger than the page width minus
412 left and right rule widths. */
414 tab_natural_width (struct tab_table *t, struct outp_driver *d, int c)
418 assert (t != NULL && c >= 0 && c < t->nc);
422 for (width = r = 0; r < t->nr; r++)
424 struct outp_text text;
425 unsigned char opt = t->ct[c + r * t->cf];
428 if (opt & (TAB_JOIN | TAB_EMPTY))
431 text.string = t->cc[c + r * t->cf];
432 text.justification = OUTP_LEFT;
433 text.font = options_to_font (opt);
434 text.h = text.v = INT_MAX;
436 d->class->text_metrics (d, &text, &w, NULL);
444 /* FIXME: This is an ugly kluge to compensate for the fact
445 that we don't let joined cells contribute to column
447 width = d->prop_em_width * 8;
451 const int clamp = d->width - t->wrv[0] - t->wrv[t->nc];
460 /* Returns the natural height of row R in table T for driver D, that
461 is, the minimum height necessary to display the information in the
462 cell at the widths set for each column. */
464 tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
468 assert (t != NULL && r >= 0 && r < t->nr);
473 for (height = d->font_height, c = 0; c < t->nc; c++)
475 struct outp_text text;
476 unsigned char opt = t->ct[c + r * t->cf];
479 if (opt & (TAB_JOIN | TAB_EMPTY))
482 text.string = t->cc[c + r * t->cf];
483 text.justification = OUTP_LEFT;
484 text.font = options_to_font (opt);
487 d->class->text_metrics (d, &text, NULL, &h);
497 /* Callback function to set all columns and rows to their natural
498 dimensions. Not really meant to be called directly. */
500 tab_natural_dimensions (struct tab_table *t, struct outp_driver *d,
507 for (i = 0; i < t->nc; i++)
508 t->w[i] = tab_natural_width (t, d, i);
510 for (i = 0; i < t->nr; i++)
511 t->h[i] = tab_natural_height (t, d, i);
517 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
518 from V, displayed with format spec F. */
520 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
521 const union value *v, const struct fmt_spec *f)
525 assert (table != NULL && v != NULL && f != NULL);
527 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
528 || c + table->col_ofs >= table->nc
529 || r + table->row_ofs >= table->nr)
531 printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
533 c, table->col_ofs, c + table->col_ofs,
534 r, table->row_ofs, r + table->row_ofs,
535 table->nc, table->nr);
540 contents = data_out_pool (v, f, table->container);
542 table->cc[c + r * table->cf] = ss_cstr (contents);
543 table->ct[c + r * table->cf] = opt;
546 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
547 with NDEC decimal places. */
549 tab_fixed (struct tab_table *table, int c, int r, unsigned char opt,
550 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 s = data_out_pool (&double_value, &f, table->container);
584 while (isspace ((unsigned char) *cp) && cp < &s[w])
588 table->cc[c + r * table->cf] = ss_buffer (cp, f.w);
589 table->ct[c + r * table->cf] = opt;
592 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL as
594 If FMT is null, then the default print format will be used.
597 tab_double (struct tab_table *table, int c, int r, unsigned char opt,
598 double val, const struct fmt_spec *fmt)
603 union value double_value ;
605 assert (table != NULL);
608 assert (c < table->nc);
610 assert (r < table->nr);
613 fmt = settings_get_format ();
615 fmt_check_output (fmt);
618 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
619 || c + table->col_ofs >= table->nc
620 || r + table->row_ofs >= table->nr)
622 printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
624 c, table->col_ofs, c + table->col_ofs,
625 r, table->row_ofs, r + table->row_ofs,
626 table->nc, table->nr);
631 double_value.f = val;
632 s = data_out_pool (&double_value, fmt, table->container);
635 while (isspace ((unsigned char) *cp) && cp < s + fmt->w)
639 w = fmt->w - (cp - s);
641 table->cc[c + r * table->cf] = ss_buffer (cp, w);
642 table->ct[c + r * table->cf] = opt;
646 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
649 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
653 assert (table != NULL && text != NULL);
657 assert (c < table->nc);
658 assert (r < table->nr);
662 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
663 || c + table->col_ofs >= table->nc
664 || r + table->row_ofs >= table->nr)
666 printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
668 c, table->col_ofs, c + table->col_ofs,
669 r, table->row_ofs, r + table->row_ofs,
670 table->nc, table->nr);
675 va_start (args, text);
676 table->cc[c + r * table->cf] = text_format (table, opt, text, args);
677 table->ct[c + r * table->cf] = opt;
681 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
682 options OPT to have text value TEXT. */
684 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
685 unsigned opt, const char *text, ...)
687 struct tab_joined_cell *j;
689 assert (table != NULL && text != NULL);
691 assert (x1 + table->col_ofs >= 0);
692 assert (y1 + table->row_ofs >= 0);
695 assert (y2 + table->row_ofs < table->nr);
696 assert (x2 + table->col_ofs < table->nc);
699 if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
700 || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
701 || x2 < x1 || x2 + table->col_ofs >= table->nc
702 || y2 < y2 || y2 + table->row_ofs >= table->nr)
704 printf ("tab_joint_text(): bad cell "
705 "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
706 x1, table->col_ofs, x1 + table->col_ofs,
707 y1, table->row_ofs, y1 + table->row_ofs,
708 x2, table->col_ofs, x2 + table->col_ofs,
709 y2, table->row_ofs, y2 + table->row_ofs,
710 table->nc, table->nr);
715 tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
717 j = pool_alloc (table->container, sizeof *j);
719 j->x1 = x1 + table->col_ofs;
720 j->y1 = y1 + table->row_ofs;
721 j->x2 = ++x2 + table->col_ofs;
722 j->y2 = ++y2 + table->row_ofs;
727 va_start (args, text);
728 j->contents = text_format (table, opt, text, args);
735 struct substring *cc = &table->cc[x1 + y1 * table->cf];
736 unsigned char *ct = &table->ct[x1 + y1 * table->cf];
737 const int ofs = table->cf - (x2 - x1);
741 for (y = y1; y < y2; y++)
745 for (x = x1; x < x2; x++)
747 *cc++ = ss_buffer ((char *) j, 0);
757 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
759 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
760 struct substring *string)
762 assert (table != NULL && string != NULL);
765 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
766 || c + table->col_ofs >= table->nc
767 || r + table->row_ofs >= table->nr)
769 printf ("tab_raw(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
771 c, table->col_ofs, c + table->col_ofs,
772 r, table->row_ofs, r + table->row_ofs,
773 table->nc, table->nr);
778 table->cc[c + r * table->cf] = *string;
779 table->ct[c + r * table->cf] = opt;
784 /* Sets the widths of all the columns and heights of all the rows in
785 table T for driver D. */
787 nowrap_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
789 t->w[0] = tab_natural_width (t, d, 0);
790 t->h[0] = d->font_height;
793 /* Sets the widths of all the columns and heights of all the rows in
794 table T for driver D. */
796 wrap_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
798 t->w[0] = tab_natural_width (t, d, 0);
799 t->h[0] = tab_natural_height (t, d, 0);
802 /* Outputs text BUF as a table with a single cell having cell options
803 OPTIONS, which is a combination of the TAB_* and TAT_*
806 tab_output_text (int options, const char *buf, ...)
808 struct tab_table *t = tab_create (1, 1, 0);
809 char *tmp_buf = NULL;
811 if (options & TAT_PRINTF)
815 va_start (args, buf);
816 buf = tmp_buf = xvasprintf (buf, args);
820 tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
821 tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
822 tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL);
828 /* Set table flags to FLAGS. */
830 tab_flags (struct tab_table *t, unsigned flags)
836 /* Easy, type-safe way to submit a tab table to som. */
838 tab_submit (struct tab_table *t)
843 s.class = &tab_table_class;
852 /* Set table row and column offsets for all functions that affect
855 tab_offset (struct tab_table *t, int col, int row)
861 if (row < -1 || row > t->nr)
863 printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
866 if (col < -1 || col > t->nc)
868 printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
874 diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
876 diff += (col - t->col_ofs), t->col_ofs = col;
882 /* Increment the row offset by one. If the table is too small,
883 increase its size. */
885 tab_next_row (struct tab_table *t)
890 if (++t->row_ofs >= t->nr)
891 tab_realloc (t, -1, t->nr * 4 / 3);
894 static struct tab_table *t;
895 static struct outp_driver *d;
898 /* Set the current table to TABLE. */
900 tabi_table (struct som_entity *table)
902 assert (table != NULL);
903 assert (table->type == SOM_TABLE);
906 tab_offset (t, 0, 0);
908 assert (t->w == NULL && t->h == NULL);
909 t->w = pool_nalloc (t->container, t->nc, sizeof *t->w);
910 t->h = pool_nalloc (t->container, t->nr, sizeof *t->h);
911 t->hrh = pool_nmalloc (t->container, t->nr + 1, sizeof *t->hrh);
912 t->wrv = pool_nmalloc (t->container, t->nc + 1, sizeof *t->wrv);
915 /* Returns the line style to use for spacing purposes for a rule
916 of the given TYPE. */
917 static enum outp_line_style
918 rule_to_spacing_type (unsigned char type)
926 return OUTP_L_SINGLE;
928 return OUTP_L_DOUBLE;
934 /* Set the current output device to DRIVER. */
936 tabi_driver (struct outp_driver *driver)
941 assert (driver != NULL);
944 /* Figure out sizes of rules. */
945 for (r = 0; r <= t->nr; r++)
948 for (c = 0; c < t->nc; c++)
950 unsigned char rh = t->rh[c + r * t->cf];
951 int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
958 for (c = 0; c <= t->nc; c++)
961 for (r = 0; r < t->nr; r++)
963 unsigned char *rv = &t->rv[c + r * (t->cf + 1)];
965 if (*rv == UCHAR_MAX)
966 *rv = c != 0 && c != t->nc ? TAL_GAP : TAL_0;
967 w = driver->vert_line_width[rule_to_spacing_type (*rv)];
975 for (i = 0; i < t->nr; i++)
977 for (i = 0; i < t->nc; i++)
981 assert (t->dim != NULL);
982 t->dim (t, d, t->dim_aux);
988 for (i = 0; i < t->nr; i++)
992 printf ("Table row %d height not initialized.\n", i);
995 assert (t->h[i] > 0);
998 for (i = 0; i < t->nc; i++)
1002 printf ("Table column %d width not initialized.\n", i);
1005 assert (t->w[i] > 0);
1010 /* Add up header sizes. */
1011 for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
1012 t->wl += t->w[i] + t->wrv[i + 1];
1013 for (i = 0, t->ht = t->hrh[0]; i < t->t; i++)
1014 t->ht += t->h[i] + t->hrh[i + 1];
1015 for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++)
1016 t->wr += t->w[i] + t->wrv[i + 1];
1017 for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
1018 t->hb += t->h[i] + t->hrh[i + 1];
1021 if (!(t->flags & SOMF_NO_TITLE))
1022 t->ht += d->font_height;
1025 /* Return the number of columns and rows in the table into N_COLUMNS
1026 and N_ROWS, respectively. */
1028 tabi_count (int *n_columns, int *n_rows)
1030 assert (n_columns != NULL && n_rows != NULL);
1035 static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual);
1037 /* Return the horizontal and vertical size of the entire table,
1038 including headers, for the current output device, into HORIZ and
1041 tabi_area (int *horiz, int *vert)
1043 assert (horiz != NULL && vert != NULL);
1048 for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
1049 c < t->nc - t->r; c++)
1050 w += t->w[c] + t->wrv[c];
1056 for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
1057 r < t->nr - t->b; r++)
1058 h += t->h[r] + t->hrh[r];
1063 /* Return the column style for this table into STYLE. */
1065 tabi_columns (int *style)
1067 assert (style != NULL);
1068 *style = t->col_style;
1071 /* Return the number of header rows/columns on the left, right, top,
1072 and bottom sides into HL, HR, HT, and HB, respectively. */
1074 tabi_headers (int *hl, int *hr, int *ht, int *hb)
1076 assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL);
1083 /* Determines the number of rows or columns (including appropriate
1084 headers), depending on CUMTYPE, that will fit into the space
1085 specified. Takes rows/columns starting at index START and attempts
1086 to fill up available space MAX. Returns in END the index of the
1087 last row/column plus one; returns in ACTUAL the actual amount of
1088 space the selected rows/columns (including appropriate headers)
1091 tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
1098 assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1099 if (cumtype == SOM_ROWS)
1101 assert (start >= 0 && start < t->nr);
1104 r = &t->hrh[start + 1];
1105 total = t->ht + t->hb;
1109 assert (start >= 0 && start < t->nc);
1112 r = &t->wrv[start + 1];
1113 total = t->wl + t->wr;
1129 for (x = start + 1; x < n; x++)
1131 int amt = *d++ + *r++;
1149 /* Return flags set for the current table into FLAGS. */
1151 tabi_flags (unsigned *flags)
1153 assert (flags != NULL);
1157 /* Returns true if the table will fit in the given page WIDTH,
1160 tabi_fits_width (int width)
1164 for (i = t->l; i < t->nc - t->r; i++)
1165 if (t->wl + t->wr + t->w[i] > width)
1171 /* Returns true if the table will fit in the given page LENGTH,
1174 tabi_fits_length (int length)
1178 for (i = t->t; i < t->nr - t->b; i++)
1179 if (t->ht + t->hb + t->h[i] > length)
1185 /* Sets the number of header rows/columns on the left, right, top,
1186 and bottom sides to HL, HR, HT, and HB, respectively. */
1188 tabi_set_headers (int hl, int hr, int ht, int hb)
1196 /* Render title for current table, with major index X and minor index
1197 Y. Y may be zero, or X and Y may be zero, but X should be nonzero
1200 tabi_title (int x, int y)
1205 if (t->flags & SOMF_NO_TITLE)
1208 cp = spprintf (buf, "%d.%d", table_num, subtable_num);
1210 cp = spprintf (cp, "(%d:%d)", x, y);
1212 cp = spprintf (cp, "(%d)", x);
1213 if (command_name != NULL)
1214 cp = spprintf (cp, " %s", command_name);
1215 cp = stpcpy (cp, ". ");
1216 if (t->title != NULL)
1218 size_t length = strlen (t->title);
1219 memcpy (cp, t->title, length);
1225 struct outp_text text;
1227 text.font = OUTP_PROPORTIONAL;
1228 text.justification = OUTP_LEFT;
1229 text.string = ss_buffer (buf, cp - buf);
1231 text.v = d->font_height;
1234 d->class->text_draw (d, &text);
1238 static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
1240 /* Renders columns C0...C1, plus headers, of rows R0...R1,
1241 at the given vertical position Y.
1242 C0 and C1 count vertical rules as columns,
1243 but R0 and R1 do not count horizontal rules as rows.
1244 Returns the vertical position after rendering. */
1246 render_rows (int y, int c0, int c1, int r0, int r1)
1249 for (r = r0; r < r1; r++)
1252 x = render_strip (x, y, r, 0, t->l * 2 + 1, r0, r1);
1253 x = render_strip (x, y, r, c0 * 2 + 1, c1 * 2, r0, r1);
1254 x = render_strip (x, y, r, (t->nc - t->r) * 2, t->nc * 2 + 1, r0, r1);
1255 y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2];
1260 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1261 current position on the current output device. */
1263 tabi_render (int c0, int r0, int c1, int r1)
1270 if (!(t->flags & SOMF_NO_TITLE))
1271 y += d->font_height;
1273 y = render_rows (y, c0, c1, 0, t->t * 2 + 1);
1274 y = render_rows (y, c0, c1, r0 * 2 + 1, r1 * 2);
1275 y = render_rows (y, c0, c1, (t->nr - t->b) * 2, t->nr * 2 + 1);
1278 const struct som_table_class tab_table_class =
1304 static enum outp_justification
1305 translate_justification (unsigned int opt)
1307 switch (opt & TAB_ALIGN_MASK)
1320 /* Returns the line style to use for drawing a rule of the given
1322 static enum outp_line_style
1323 rule_to_draw_type (unsigned char type)
1331 return OUTP_L_SINGLE;
1333 return OUTP_L_DOUBLE;
1339 /* Returns the horizontal rule at the given column and row. */
1341 get_hrule (int c, int r)
1343 return t->rh[c + r * t->cf];
1346 /* Returns the vertical rule at the given column and row. */
1348 get_vrule (int c, int r)
1350 return t->rv[c + r * (t->cf + 1)];
1353 /* Renders the horizontal rule at the given column and row
1354 at (X,Y) on the page. */
1356 render_horz_rule (int x, int y, int c, int r)
1358 enum outp_line_style style = rule_to_draw_type (get_hrule (c, r));
1359 if (style != OUTP_L_NONE)
1360 d->class->line (d, x, y, x + t->w[c], y + t->hrh[r],
1361 OUTP_L_NONE, style, OUTP_L_NONE, style);
1364 /* Renders the vertical rule at the given column and row
1365 at (X,Y) on the page. */
1367 render_vert_rule (int x, int y, int c, int r)
1369 enum outp_line_style style = rule_to_draw_type (get_vrule (c, r));
1370 if (style != OUTP_L_NONE)
1371 d->class->line (d, x, y, x + t->wrv[c], y + t->h[r],
1372 style, OUTP_L_NONE, style, OUTP_L_NONE);
1375 /* Renders the rule intersection at the given column and row
1376 at (X,Y) on the page. */
1378 render_rule_intersection (int x, int y, int c, int r)
1380 /* Bounds of intersection. */
1383 int x1 = x + t->wrv[c];
1384 int y1 = y + t->hrh[r];
1386 /* Lines on each side of intersection. */
1387 int top = r > 0 ? get_vrule (c, r - 1) : TAL_0;
1388 int left = c > 0 ? get_hrule (c - 1, r) : TAL_0;
1389 int bottom = r < t->nr ? get_vrule (c, r) : TAL_0;
1390 int right = c < t->nc ? get_hrule (c, r) : TAL_0;
1392 /* Output style for each line. */
1393 enum outp_line_style o_top = rule_to_draw_type (top);
1394 enum outp_line_style o_left = rule_to_draw_type (left);
1395 enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1396 enum outp_line_style o_right = rule_to_draw_type (right);
1398 if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1399 || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1400 d->class->line (d, x0, y0, x1, y1, o_top, o_left, o_bottom, o_right);
1403 /* Returns the width of columns C1...C2 exclusive,
1404 including interior but not exterior rules. */
1406 strip_width (int c1, int c2)
1411 for (c = c1; c < c2; c++)
1412 width += t->w[c] + t->wrv[c + 1];
1414 width -= t->wrv[c2];
1418 /* Returns the height of rows R1...R2 exclusive,
1419 including interior but not exterior rules. */
1421 strip_height (int r1, int r2)
1426 for (r = r1; r < r2; r++)
1427 height += t->h[r] + t->hrh[r + 1];
1429 height -= t->hrh[r2];
1433 /* Renders the cell at the given column and row at (X,Y) on the
1434 page. Also renders joined cells that extend as far to the
1435 right as C1 and as far down as R1. */
1437 render_cell (int x, int y, int c, int r, int c1, int r1)
1439 const int index = c + (r * t->cf);
1440 unsigned char type = t->ct[index];
1441 struct substring *content = &t->cc[index];
1443 if (!(type & TAB_JOIN))
1445 if (!(type & TAB_EMPTY))
1447 struct outp_text text;
1448 text.font = options_to_font (type);
1449 text.justification = translate_justification (type);
1450 text.string = *content;
1455 d->class->text_draw (d, &text);
1460 struct tab_joined_cell *j
1461 = (struct tab_joined_cell *) ss_data (*content);
1463 if (j->hit != tab_hit)
1467 if (j->x1 == c && j->y1 == r)
1469 struct outp_text text;
1470 text.font = options_to_font (type);
1471 text.justification = translate_justification (type);
1472 text.string = j->contents;
1475 text.h = strip_width (j->x1, MIN (j->x2, c1));
1476 text.v = strip_height (j->y1, MIN (j->y2, r1));
1477 d->class->text_draw (d, &text);
1483 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1484 on row R, at (X,Y). Returns X position after rendering.
1485 Also renders joined cells that extend beyond that strip,
1486 cropping them to lie within rendering region (C0,R0)-(C1,R1).
1487 C0 and C1 count vertical rules as columns.
1488 R counts horizontal rules as rows, but R0 and R1 do not. */
1490 render_strip (int x, int y, int r, int c0, int c1, int r0 UNUSED, int r1)
1494 for (c = c0; c < c1; c++)
1498 render_cell (x, y, c / 2, r / 2, c1 / 2, r1);
1500 render_horz_rule (x, y, c / 2, r / 2);
1506 render_vert_rule (x, y, c / 2, r / 2);
1508 render_rule_intersection (x, y, c / 2, r / 2);
1515 /* Sets COMMAND_NAME as the name of the current command,
1516 for embedding in output. */
1518 tab_set_command_name (const char *command_name_)
1520 free (command_name);
1521 command_name = command_name_ ? xstrdup (command_name_) : NULL;