1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
29 #include <data/data-out.h>
30 #include <data/format.h>
31 #include <data/value.h>
32 #include <libpspp/assertion.h>
33 #include <libpspp/compiler.h>
34 #include <libpspp/misc.h>
35 #include <libpspp/pool.h>
41 #define _(msgid) gettext (msgid)
43 const struct som_table_class tab_table_class;
44 static char *command_name;
46 /* Returns the font to use for a cell with the given OPTIONS. */
48 options_to_font (unsigned options)
50 return (options & TAB_FIX ? OUTP_FIXED
51 : options & TAB_EMPH ? OUTP_EMPHASIS
55 /* Creates a table with NC columns and NR rows. */
57 tab_create (int nc, int nr, int reallocable UNUSED)
61 t = pool_create_container (struct tab_table, container);
62 t->col_style = TAB_COL_NONE;
68 t->l = t->r = t->t = t->b = 0;
70 t->cc = pool_nmalloc (t->container, nr * nc, sizeof *t->cc);
71 t->ct = pool_malloc (t->container, nr * nc);
72 memset (t->ct, TAB_EMPTY, nc * nr);
74 t->rh = pool_nmalloc (t->container, nc, nr + 1);
75 memset (t->rh, 0, nc * (nr + 1));
77 t->rv = pool_nmalloc (t->container, nr, nc + 1);
78 memset (t->rv, UCHAR_MAX, nr * (nc + 1));
82 t->col_ofs = t->row_ofs = 0;
87 /* Destroys table T. */
89 tab_destroy (struct tab_table *t)
93 pool_destroy (t->container);
96 /* Sets the width and height of a table, in columns and rows,
97 respectively. Use only to reduce the size of a table, since it
98 does not change the amount of allocated memory. */
100 tab_resize (struct tab_table *t, int nc, int nr)
105 assert (nc + t->col_ofs <= t->cf);
106 t->nc = nc + t->col_ofs;
110 assert (nr + t->row_ofs <= t->nr);
111 t->nr = nr + t->row_ofs;
115 /* Changes either or both dimensions of a table. Consider using the
116 above routine instead if it won't waste a lot of space.
118 Changing the number of columns in a table is particularly expensive
119 in space and time. Avoid doing such. FIXME: In fact, transferring
120 of rules isn't even implemented yet. */
122 tab_realloc (struct tab_table *t, int nc, int nr)
130 tab_offset (t, 0, 0);
137 assert (nc == t->nc);
141 int mr1 = MIN (nr, t->nr);
142 int mc1 = MIN (nc, t->nc);
144 struct substring *new_cc;
145 unsigned char *new_ct;
148 new_cc = pool_nmalloc (t->container, nr * nc, sizeof *new_cc);
149 new_ct = pool_malloc (t->container, nr * nc);
150 for (r = 0; r < mr1; r++)
152 memcpy (&new_cc[r * nc], &t->cc[r * t->nc], mc1 * sizeof *t->cc);
153 memcpy (&new_ct[r * nc], &t->ct[r * t->nc], mc1);
154 memset (&new_ct[r * nc + t->nc], TAB_EMPTY, nc - t->nc);
156 pool_free (t->container, t->cc);
157 pool_free (t->container, t->ct);
162 else if (nr != t->nr)
164 t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc);
165 t->ct = pool_realloc (t->container, t->ct, nr * nc);
167 t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
168 t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
172 memset (&t->rh[nc * (t->nr + 1)], TAL_0, (nr - t->nr) * nc);
173 memset (&t->rv[(nc + 1) * t->nr], UCHAR_MAX,
174 (nr - t->nr) * (nc + 1));
178 memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr));
184 tab_offset (t, co, ro);
187 /* Sets the number of header rows on each side of TABLE to L on the
188 left, R on the right, T on the top, B on the bottom. Header rows
189 are repeated when a table is broken across multiple columns or
192 tab_headers (struct tab_table *table, int l, int r, int t, int b)
194 assert (table != NULL);
195 assert (l < table->nc);
196 assert (r < table->nc);
197 assert (t < table->nr);
198 assert (b < table->nr);
207 /* Set up table T so that, when it is an appropriate size, it will be
208 displayed across the page in columns.
210 STYLE is a TAB_COL_* constant. GROUP is the number of rows to take
213 tab_columns (struct tab_table *t, int style, int group)
216 t->col_style = style;
217 t->col_group = group;
222 /* Draws a vertical line to the left of cells at horizontal position X
223 from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
225 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
230 if (x + t->col_ofs < 0 || x + t->col_ofs > t->nc
231 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
232 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
234 printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
235 "table size (%d,%d)\n"),
236 x, t->col_ofs, x + t->col_ofs,
237 y1, t->row_ofs, y1 + t->row_ofs,
238 y2, t->row_ofs, y2 + t->row_ofs,
252 assert (y2 <= t->nr);
257 for (y = y1; y <= y2; y++)
258 t->rv[x + (t->cf + 1) * y] = style;
262 /* Draws a horizontal line above cells at vertical position Y from X1
263 to X2 inclusive in style STYLE, if style is not -1. */
265 tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
282 for (x = x1; x <= x2; x++)
283 t->rh[x + t->cf * y] = style;
287 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
288 lines of style F_H and vertical lines of style F_V. Fills the
289 interior of the box with horizontal lines of style I_H and vertical
290 lines of style I_V. Any of the line styles may be -1 to avoid
291 drawing those lines. This is distinct from 0, which draws a null
294 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
295 int x1, int y1, int x2, int y2)
300 if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc
301 || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= t->nc
302 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
303 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
305 printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
306 "in table size (%d,%d)\n"),
307 x1, t->col_ofs, x1 + t->col_ofs,
308 y1, t->row_ofs, y1 + t->row_ofs,
309 x2, t->col_ofs, x2 + t->col_ofs,
310 y2, t->row_ofs, y2 + t->row_ofs,
331 for (x = x1; x <= x2; x++)
333 t->rh[x + t->cf * y1] = f_h;
334 t->rh[x + t->cf * (y2 + 1)] = f_h;
340 for (y = y1; y <= y2; y++)
342 t->rv[x1 + (t->cf + 1) * y] = f_v;
343 t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
351 for (y = y1 + 1; y <= y2; y++)
355 for (x = x1; x <= x2; x++)
356 t->rh[x + t->cf * y] = i_h;
363 for (x = x1 + 1; x <= x2; x++)
367 for (y = y1; y <= y2; y++)
368 t->rv[x + (t->cf + 1) * y] = i_v;
373 /* Formats text TEXT and arguments ARGS as indicated in OPT in
374 TABLE's pool and returns the resultant string. */
375 static struct substring
376 text_format (struct tab_table *table, int opt, const char *text, va_list args)
378 assert (table != NULL && text != NULL);
380 return ss_cstr (opt & TAT_PRINTF
381 ? pool_vasprintf (table->container, text, args)
382 : pool_strdup (table->container, text));
385 /* Set the title of table T to TITLE, which is formatted as if
386 passed to printf(). */
388 tab_title (struct tab_table *t, const char *title, ...)
392 assert (t != NULL && title != NULL);
393 va_start (args, title);
394 t->title = xvasprintf (title, args);
398 /* Set DIM_FUNC as the dimension function for table T. */
400 tab_dim (struct tab_table *t, tab_dim_func *dim_func)
402 assert (t != NULL && t->dim == NULL);
406 /* Returns the natural width of column C in table T for driver D, that
407 is, the smallest width necessary to display all its cells without
408 wrapping. The width will be no larger than the page width minus
409 left and right rule widths. */
411 tab_natural_width (struct tab_table *t, struct outp_driver *d, int c)
415 assert (t != NULL && c >= 0 && c < t->nc);
419 for (width = r = 0; r < t->nr; r++)
421 struct outp_text text;
422 unsigned char opt = t->ct[c + r * t->cf];
425 if (opt & (TAB_JOIN | TAB_EMPTY))
428 text.string = t->cc[c + r * t->cf];
429 text.justification = OUTP_LEFT;
430 text.font = options_to_font (opt);
431 text.h = text.v = INT_MAX;
433 d->class->text_metrics (d, &text, &w, NULL);
441 /* FIXME: This is an ugly kluge to compensate for the fact
442 that we don't let joined cells contribute to column
444 width = d->prop_em_width * 8;
448 const int clamp = d->width - t->wrv[0] - t->wrv[t->nc];
457 /* Returns the natural height of row R in table T for driver D, that
458 is, the minimum height necessary to display the information in the
459 cell at the widths set for each column. */
461 tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
465 assert (t != NULL && r >= 0 && r < t->nr);
470 for (height = d->font_height, c = 0; c < t->nc; c++)
472 struct outp_text text;
473 unsigned char opt = t->ct[c + r * t->cf];
476 if (opt & (TAB_JOIN | TAB_EMPTY))
479 text.string = t->cc[c + r * t->cf];
480 text.justification = OUTP_LEFT;
481 text.font = options_to_font (opt);
484 d->class->text_metrics (d, &text, NULL, &h);
494 /* Callback function to set all columns and rows to their natural
495 dimensions. Not really meant to be called directly. */
497 tab_natural_dimensions (struct tab_table *t, struct outp_driver *d)
503 for (i = 0; i < t->nc; i++)
504 t->w[i] = tab_natural_width (t, d, i);
506 for (i = 0; i < t->nr; i++)
507 t->h[i] = tab_natural_height (t, d, i);
513 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
514 from V, displayed with format spec F. */
516 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
517 const union value *v, const struct fmt_spec *f)
521 assert (table != NULL && v != NULL && f != NULL);
523 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
524 || c + table->col_ofs >= table->nc
525 || r + table->row_ofs >= table->nr)
527 printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
529 c, table->col_ofs, c + table->col_ofs,
530 r, table->row_ofs, r + table->row_ofs,
531 table->nc, table->nr);
536 contents = pool_alloc (table->container, f->w);
537 table->cc[c + r * table->cf] = ss_buffer (contents, f->w);
538 table->ct[c + r * table->cf] = opt;
540 data_out (v, f, contents);
543 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
544 with NDEC decimal places. */
546 tab_float (struct tab_table *table, int c, int r, unsigned char opt,
547 double val, int w, int d)
553 union value double_value;
555 assert (table != NULL && w <= 40);
558 assert (c < table->nc);
560 assert (r < table->nr);
562 f = fmt_for_output (FMT_F, w, d);
565 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
566 || c + table->col_ofs >= table->nc
567 || r + table->row_ofs >= table->nr)
569 printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
571 c, table->col_ofs, c + table->col_ofs,
572 r, table->row_ofs, r + table->row_ofs,
573 table->nc, table->nr);
578 double_value.f = val;
579 data_out (&double_value, &f, buf);
582 while (isspace ((unsigned char) *cp) && cp < &buf[w])
584 f.w = w - (cp - buf);
586 contents = pool_alloc (table->container, f.w);
587 table->cc[c + r * table->cf] = ss_buffer (contents, f.w);
588 table->ct[c + r * table->cf] = opt;
589 memcpy (contents, cp, f.w);
592 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
595 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
599 assert (table != NULL && text != NULL);
603 assert (c < table->nc);
604 assert (r < table->nr);
608 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
609 || c + table->col_ofs >= table->nc
610 || r + table->row_ofs >= table->nr)
612 printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
614 c, table->col_ofs, c + table->col_ofs,
615 r, table->row_ofs, r + table->row_ofs,
616 table->nc, table->nr);
621 va_start (args, text);
622 table->cc[c + r * table->cf] = text_format (table, opt, text, args);
623 table->ct[c + r * table->cf] = opt;
627 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
628 options OPT to have text value TEXT. */
630 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
631 unsigned opt, const char *text, ...)
633 struct tab_joined_cell *j;
635 assert (table != NULL && text != NULL);
637 assert (x1 + table->col_ofs >= 0);
638 assert (y1 + table->row_ofs >= 0);
641 assert (y2 + table->row_ofs < table->nr);
642 assert (x2 + table->col_ofs < table->nc);
645 if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
646 || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
647 || x2 < x1 || x2 + table->col_ofs >= table->nc
648 || y2 < y2 || y2 + table->row_ofs >= table->nr)
650 printf ("tab_joint_text(): bad cell "
651 "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
652 x1, table->col_ofs, x1 + table->col_ofs,
653 y1, table->row_ofs, y1 + table->row_ofs,
654 x2, table->col_ofs, x2 + table->col_ofs,
655 y2, table->row_ofs, y2 + table->row_ofs,
656 table->nc, table->nr);
661 tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
663 j = pool_alloc (table->container, sizeof *j);
665 j->x1 = x1 + table->col_ofs;
666 j->y1 = y1 + table->row_ofs;
667 j->x2 = ++x2 + table->col_ofs;
668 j->y2 = ++y2 + table->row_ofs;
673 va_start (args, text);
674 j->contents = text_format (table, opt, text, args);
681 struct substring *cc = &table->cc[x1 + y1 * table->cf];
682 unsigned char *ct = &table->ct[x1 + y1 * table->cf];
683 const int ofs = table->cf - (x2 - x1);
687 for (y = y1; y < y2; y++)
691 for (x = x1; x < x2; x++)
693 *cc++ = ss_buffer ((char *) j, 0);
703 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
705 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
706 struct substring *string)
708 assert (table != NULL && string != NULL);
711 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
712 || c + table->col_ofs >= table->nc
713 || r + table->row_ofs >= table->nr)
715 printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
717 c, table->col_ofs, c + table->col_ofs,
718 r, table->row_ofs, r + table->row_ofs,
719 table->nc, table->nr);
724 table->cc[c + r * table->cf] = *string;
725 table->ct[c + r * table->cf] = opt;
730 /* Sets the widths of all the columns and heights of all the rows in
731 table T for driver D. */
733 nowrap_dim (struct tab_table *t, struct outp_driver *d)
735 t->w[0] = tab_natural_width (t, d, 0);
736 t->h[0] = d->font_height;
739 /* Sets the widths of all the columns and heights of all the rows in
740 table T for driver D. */
742 wrap_dim (struct tab_table *t, struct outp_driver *d)
744 t->w[0] = tab_natural_width (t, d, 0);
745 t->h[0] = tab_natural_height (t, d, 0);
748 /* Outputs text BUF as a table with a single cell having cell options
749 OPTIONS, which is a combination of the TAB_* and TAT_*
752 tab_output_text (int options, const char *buf, ...)
754 struct tab_table *t = tab_create (1, 1, 0);
755 char *tmp_buf = NULL;
757 if (options & TAT_PRINTF)
761 va_start (args, buf);
762 buf = tmp_buf = xvasprintf (buf, args);
766 tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
767 tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
768 tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim);
774 /* Set table flags to FLAGS. */
776 tab_flags (struct tab_table *t, unsigned flags)
782 /* Easy, type-safe way to submit a tab table to som. */
784 tab_submit (struct tab_table *t)
789 s.class = &tab_table_class;
798 /* Set table row and column offsets for all functions that affect
801 tab_offset (struct tab_table *t, int col, int row)
807 if (row < -1 || row > t->nr)
809 printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
812 if (col < -1 || col > t->nc)
814 printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
820 diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
822 diff += (col - t->col_ofs), t->col_ofs = col;
828 /* Increment the row offset by one. If the table is too small,
829 increase its size. */
831 tab_next_row (struct tab_table *t)
836 if (++t->row_ofs >= t->nr)
837 tab_realloc (t, -1, t->nr * 4 / 3);
840 static struct tab_table *t;
841 static struct outp_driver *d;
844 /* Set the current table to TABLE. */
846 tabi_table (struct som_entity *table)
848 assert (table != NULL);
849 assert (table->type == SOM_TABLE);
852 tab_offset (t, 0, 0);
854 assert (t->w == NULL && t->h == NULL);
855 t->w = pool_nalloc (t->container, t->nc, sizeof *t->w);
856 t->h = pool_nalloc (t->container, t->nr, sizeof *t->h);
857 t->hrh = pool_nmalloc (t->container, t->nr + 1, sizeof *t->hrh);
858 t->wrv = pool_nmalloc (t->container, t->nc + 1, sizeof *t->wrv);
861 /* Returns the line style to use for spacing purposes for a rule
862 of the given TYPE. */
863 static enum outp_line_style
864 rule_to_spacing_type (unsigned char type)
872 return OUTP_L_SINGLE;
874 return OUTP_L_DOUBLE;
880 /* Set the current output device to DRIVER. */
882 tabi_driver (struct outp_driver *driver)
887 assert (driver != NULL);
890 /* Figure out sizes of rules. */
891 for (r = 0; r <= t->nr; r++)
894 for (c = 0; c < t->nc; c++)
896 unsigned char rh = t->rh[c + r * t->cf];
897 int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
904 for (c = 0; c <= t->nc; c++)
907 for (r = 0; r < t->nr; r++)
909 unsigned char *rv = &t->rv[c + r * (t->cf + 1)];
911 if (*rv == UCHAR_MAX)
912 *rv = c != 0 && c != t->nc ? TAL_GAP : TAL_0;
913 w = driver->vert_line_width[rule_to_spacing_type (*rv)];
921 for (i = 0; i < t->nr; i++)
923 for (i = 0; i < t->nc; i++)
927 assert (t->dim != NULL);
934 for (i = 0; i < t->nr; i++)
938 printf ("Table row %d height not initialized.\n", i);
941 assert (t->h[i] > 0);
944 for (i = 0; i < t->nc; i++)
948 printf ("Table column %d width not initialized.\n", i);
951 assert (t->w[i] > 0);
956 /* Add up header sizes. */
957 for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
958 t->wl += t->w[i] + t->wrv[i + 1];
959 for (i = 0, t->ht = t->hrh[0]; i < t->t; i++)
960 t->ht += t->h[i] + t->hrh[i + 1];
961 for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++)
962 t->wr += t->w[i] + t->wrv[i + 1];
963 for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
964 t->hb += t->h[i] + t->hrh[i + 1];
967 if (!(t->flags & SOMF_NO_TITLE))
968 t->ht += d->font_height;
971 /* Return the number of columns and rows in the table into N_COLUMNS
972 and N_ROWS, respectively. */
974 tabi_count (int *n_columns, int *n_rows)
976 assert (n_columns != NULL && n_rows != NULL);
981 static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual);
983 /* Return the horizontal and vertical size of the entire table,
984 including headers, for the current output device, into HORIZ and
987 tabi_area (int *horiz, int *vert)
989 assert (horiz != NULL && vert != NULL);
994 for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
995 c < t->nc - t->r; c++)
996 w += t->w[c] + t->wrv[c];
1002 for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
1003 r < t->nr - t->b; r++)
1004 h += t->h[r] + t->hrh[r];
1009 /* Return the column style for this table into STYLE. */
1011 tabi_columns (int *style)
1013 assert (style != NULL);
1014 *style = t->col_style;
1017 /* Return the number of header rows/columns on the left, right, top,
1018 and bottom sides into HL, HR, HT, and HB, respectively. */
1020 tabi_headers (int *hl, int *hr, int *ht, int *hb)
1022 assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL);
1029 /* Determines the number of rows or columns (including appropriate
1030 headers), depending on CUMTYPE, that will fit into the space
1031 specified. Takes rows/columns starting at index START and attempts
1032 to fill up available space MAX. Returns in END the index of the
1033 last row/column plus one; returns in ACTUAL the actual amount of
1034 space the selected rows/columns (including appropriate headers)
1037 tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
1044 assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1045 if (cumtype == SOM_ROWS)
1047 assert (start >= 0 && start < t->nr);
1050 r = &t->hrh[start + 1];
1051 total = t->ht + t->hb;
1055 assert (start >= 0 && start < t->nc);
1058 r = &t->wrv[start + 1];
1059 total = t->wl + t->wr;
1075 for (x = start + 1; x < n; x++)
1077 int amt = *d++ + *r++;
1095 /* Return flags set for the current table into FLAGS. */
1097 tabi_flags (unsigned *flags)
1099 assert (flags != NULL);
1103 /* Returns true if the table will fit in the given page WIDTH,
1106 tabi_fits_width (int width)
1110 for (i = t->l; i < t->nc - t->r; i++)
1111 if (t->wl + t->wr + t->w[i] > width)
1117 /* Returns true if the table will fit in the given page LENGTH,
1120 tabi_fits_length (int length)
1124 for (i = t->t; i < t->nr - t->b; i++)
1125 if (t->ht + t->hb + t->h[i] > length)
1131 /* Sets the number of header rows/columns on the left, right, top,
1132 and bottom sides to HL, HR, HT, and HB, respectively. */
1134 tabi_set_headers (int hl, int hr, int ht, int hb)
1142 /* Render title for current table, with major index X and minor index
1143 Y. Y may be zero, or X and Y may be zero, but X should be nonzero
1146 tabi_title (int x, int y)
1151 if (t->flags & SOMF_NO_TITLE)
1154 cp = spprintf (buf, "%d.%d", table_num, subtable_num);
1156 cp = spprintf (cp, "(%d:%d)", x, y);
1158 cp = spprintf (cp, "(%d)", x);
1159 if (command_name != NULL)
1160 cp = spprintf (cp, " %s", command_name);
1161 cp = stpcpy (cp, ". ");
1162 if (t->title != NULL)
1164 size_t length = strlen (t->title);
1165 memcpy (cp, t->title, length);
1171 struct outp_text text;
1173 text.font = OUTP_PROPORTIONAL;
1174 text.justification = OUTP_LEFT;
1175 text.string = ss_buffer (buf, cp - buf);
1177 text.v = d->font_height;
1180 d->class->text_draw (d, &text);
1184 static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
1186 /* Renders columns C0...C1, plus headers, of rows R0...R1,
1187 at the given vertical position Y.
1188 C0 and C1 count vertical rules as columns,
1189 but R0 and R1 do not count horizontal rules as rows.
1190 Returns the vertical position after rendering. */
1192 render_rows (int y, int c0, int c1, int r0, int r1)
1195 for (r = r0; r < r1; r++)
1198 x = render_strip (x, y, r, 0, t->l * 2 + 1, r0, r1);
1199 x = render_strip (x, y, r, c0 * 2 + 1, c1 * 2, r0, r1);
1200 x = render_strip (x, y, r, (t->nc - t->r) * 2, t->nc * 2 + 1, r0, r1);
1201 y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2];
1206 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1207 current position on the current output device. */
1209 tabi_render (int c0, int r0, int c1, int r1)
1216 if (!(t->flags & SOMF_NO_TITLE))
1217 y += d->font_height;
1219 y = render_rows (y, c0, c1, 0, t->t * 2 + 1);
1220 y = render_rows (y, c0, c1, r0 * 2 + 1, r1 * 2);
1221 y = render_rows (y, c0, c1, (t->nr - t->b) * 2, t->nr * 2 + 1);
1224 const struct som_table_class tab_table_class =
1250 static enum outp_justification
1251 translate_justification (unsigned int opt)
1253 switch (opt & TAB_ALIGN_MASK)
1266 /* Returns the line style to use for drawing a rule of the given
1268 static enum outp_line_style
1269 rule_to_draw_type (unsigned char type)
1277 return OUTP_L_SINGLE;
1279 return OUTP_L_DOUBLE;
1285 /* Returns the horizontal rule at the given column and row. */
1287 get_hrule (int c, int r)
1289 return t->rh[c + r * t->cf];
1292 /* Returns the vertical rule at the given column and row. */
1294 get_vrule (int c, int r)
1296 return t->rv[c + r * (t->cf + 1)];
1299 /* Renders the horizontal rule at the given column and row
1300 at (X,Y) on the page. */
1302 render_horz_rule (int x, int y, int c, int r)
1304 enum outp_line_style style = rule_to_draw_type (get_hrule (c, r));
1305 if (style != OUTP_L_NONE)
1306 d->class->line (d, x, y, x + t->w[c], y + t->hrh[r],
1307 OUTP_L_NONE, style, OUTP_L_NONE, style);
1310 /* Renders the vertical rule at the given column and row
1311 at (X,Y) on the page. */
1313 render_vert_rule (int x, int y, int c, int r)
1315 enum outp_line_style style = rule_to_draw_type (get_vrule (c, r));
1316 if (style != OUTP_L_NONE)
1317 d->class->line (d, x, y, x + t->wrv[c], y + t->h[r],
1318 style, OUTP_L_NONE, style, OUTP_L_NONE);
1321 /* Renders the rule intersection at the given column and row
1322 at (X,Y) on the page. */
1324 render_rule_intersection (int x, int y, int c, int r)
1326 /* Bounds of intersection. */
1329 int x1 = x + t->wrv[c];
1330 int y1 = y + t->hrh[r];
1332 /* Lines on each side of intersection. */
1333 int top = r > 0 ? get_vrule (c, r - 1) : TAL_0;
1334 int left = c > 0 ? get_hrule (c - 1, r) : TAL_0;
1335 int bottom = r < t->nr ? get_vrule (c, r) : TAL_0;
1336 int right = c < t->nc ? get_hrule (c, r) : TAL_0;
1338 /* Output style for each line. */
1339 enum outp_line_style o_top = rule_to_draw_type (top);
1340 enum outp_line_style o_left = rule_to_draw_type (left);
1341 enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1342 enum outp_line_style o_right = rule_to_draw_type (right);
1344 if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1345 || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1346 d->class->line (d, x0, y0, x1, y1, o_top, o_left, o_bottom, o_right);
1349 /* Returns the width of columns C1...C2 exclusive,
1350 including interior but not exterior rules. */
1352 strip_width (int c1, int c2)
1357 for (c = c1; c < c2; c++)
1358 width += t->w[c] + t->wrv[c + 1];
1360 width -= t->wrv[c2];
1364 /* Returns the height of rows R1...R2 exclusive,
1365 including interior but not exterior rules. */
1367 strip_height (int r1, int r2)
1372 for (r = r1; r < r2; r++)
1373 height += t->h[r] + t->hrh[r + 1];
1375 height -= t->hrh[r2];
1379 /* Renders the cell at the given column and row at (X,Y) on the
1380 page. Also renders joined cells that extend as far to the
1381 right as C1 and as far down as R1. */
1383 render_cell (int x, int y, int c, int r, int c1, int r1)
1385 const int index = c + (r * t->cf);
1386 unsigned char type = t->ct[index];
1387 struct substring *content = &t->cc[index];
1389 if (!(type & TAB_JOIN))
1391 if (!(type & TAB_EMPTY))
1393 struct outp_text text;
1394 text.font = options_to_font (type);
1395 text.justification = translate_justification (type);
1396 text.string = *content;
1401 d->class->text_draw (d, &text);
1406 struct tab_joined_cell *j
1407 = (struct tab_joined_cell *) ss_data (*content);
1409 if (j->hit != tab_hit)
1413 if (j->x1 == c && j->y1 == r)
1415 struct outp_text text;
1416 text.font = options_to_font (type);
1417 text.justification = translate_justification (type);
1418 text.string = j->contents;
1421 text.h = strip_width (j->x1, MIN (j->x2, c1));
1422 text.v = strip_height (j->y1, MIN (j->y2, r1));
1423 d->class->text_draw (d, &text);
1429 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1430 on row R, at (X,Y). Returns X position after rendering.
1431 Also renders joined cells that extend beyond that strip,
1432 cropping them to lie within rendering region (C0,R0)-(C1,R1).
1433 C0 and C1 count vertical rules as columns.
1434 R counts horizontal rules as rows, but R0 and R1 do not. */
1436 render_strip (int x, int y, int r, int c0, int c1, int r0 UNUSED, int r1)
1440 for (c = c0; c < c1; c++)
1444 render_cell (x, y, c / 2, r / 2, c1 / 2, r1);
1446 render_horz_rule (x, y, c / 2, r / 2);
1452 render_vert_rule (x, y, c / 2, r / 2);
1454 render_rule_intersection (x, y, c / 2, r / 2);
1461 /* Sets COMMAND_NAME as the name of the current command,
1462 for embedding in output. */
1464 tab_set_command_name (const char *command_name_)
1466 free (command_name);
1467 command_name = command_name_ ? xstrdup (command_name_) : NULL;