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>
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);
66 t->col_style = TAB_COL_NONE;
72 t->l = t->r = t->t = t->b = 0;
74 t->cc = pool_nmalloc (t->container, nr * nc, sizeof *t->cc);
75 t->ct = pool_malloc (t->container, nr * nc);
76 memset (t->ct, TAB_EMPTY, nc * nr);
78 t->rh = pool_nmalloc (t->container, nc, nr + 1);
79 memset (t->rh, 0, nc * (nr + 1));
81 t->rv = pool_nmalloc (t->container, nr, nc + 1);
82 memset (t->rv, UCHAR_MAX, nr * (nc + 1));
85 t->col_ofs = t->row_ofs = 0;
90 /* Increases T's reference count and, if this causes T's
91 reference count to reach 0, destroys T. */
93 tab_destroy (struct tab_table *t)
95 assert (t->ref_cnt > 0);
98 if (t->dim_free != NULL)
99 t->dim_free (t->dim_aux);
101 pool_destroy (t->container);
104 /* Increases T's reference count. */
106 tab_ref (struct tab_table *t)
108 assert (t->ref_cnt > 0);
112 /* Sets the width and height of a table, in columns and rows,
113 respectively. Use only to reduce the size of a table, since it
114 does not change the amount of allocated memory. */
116 tab_resize (struct tab_table *t, int nc, int nr)
121 assert (nc + t->col_ofs <= t->cf);
122 t->nc = nc + t->col_ofs;
126 assert (nr + t->row_ofs <= t->nr);
127 t->nr = nr + t->row_ofs;
131 /* Changes either or both dimensions of a table. Consider using the
132 above routine instead if it won't waste a lot of space.
134 Changing the number of columns in a table is particularly expensive
135 in space and time. Avoid doing such. FIXME: In fact, transferring
136 of rules isn't even implemented yet. */
138 tab_realloc (struct tab_table *t, int nc, int nr)
146 tab_offset (t, 0, 0);
153 assert (nc == t->nc);
157 int mr1 = MIN (nr, t->nr);
158 int mc1 = MIN (nc, t->nc);
160 struct substring *new_cc;
161 unsigned char *new_ct;
164 new_cc = pool_nmalloc (t->container, nr * nc, sizeof *new_cc);
165 new_ct = pool_malloc (t->container, nr * nc);
166 for (r = 0; r < mr1; r++)
168 memcpy (&new_cc[r * nc], &t->cc[r * t->nc], mc1 * sizeof *t->cc);
169 memcpy (&new_ct[r * nc], &t->ct[r * t->nc], mc1);
170 memset (&new_ct[r * nc + t->nc], TAB_EMPTY, nc - t->nc);
172 pool_free (t->container, t->cc);
173 pool_free (t->container, t->ct);
178 else if (nr != t->nr)
180 t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc);
181 t->ct = pool_realloc (t->container, t->ct, nr * nc);
183 t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
184 t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
188 memset (&t->rh[nc * (t->nr + 1)], TAL_0, (nr - t->nr) * nc);
189 memset (&t->rv[(nc + 1) * t->nr], UCHAR_MAX,
190 (nr - t->nr) * (nc + 1));
194 memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr));
200 tab_offset (t, co, ro);
203 /* Sets the number of header rows on each side of TABLE to L on the
204 left, R on the right, T on the top, B on the bottom. Header rows
205 are repeated when a table is broken across multiple columns or
208 tab_headers (struct tab_table *table, int l, int r, int t, int b)
210 assert (table != NULL);
211 assert (l < table->nc);
212 assert (r < table->nc);
213 assert (t < table->nr);
214 assert (b < table->nr);
223 /* Set up table T so that, when it is an appropriate size, it will be
224 displayed across the page in columns.
226 STYLE is a TAB_COL_* constant. GROUP is the number of rows to take
229 tab_columns (struct tab_table *t, int style, int group)
232 t->col_style = style;
233 t->col_group = group;
238 /* Draws a vertical line to the left of cells at horizontal position X
239 from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
241 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
246 if (x + t->col_ofs < 0 || x + t->col_ofs > t->nc
247 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
248 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
250 printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
251 "table size (%d,%d)\n"),
252 x, t->col_ofs, x + t->col_ofs,
253 y1, t->row_ofs, y1 + t->row_ofs,
254 y2, t->row_ofs, y2 + t->row_ofs,
268 assert (y2 <= t->nr);
273 for (y = y1; y <= y2; y++)
274 t->rv[x + (t->cf + 1) * y] = style;
278 /* Draws a horizontal line above cells at vertical position Y from X1
279 to X2 inclusive in style STYLE, if style is not -1. */
281 tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
298 for (x = x1; x <= x2; x++)
299 t->rh[x + t->cf * y] = style;
303 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
304 lines of style F_H and vertical lines of style F_V. Fills the
305 interior of the box with horizontal lines of style I_H and vertical
306 lines of style I_V. Any of the line styles may be -1 to avoid
307 drawing those lines. This is distinct from 0, which draws a null
310 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
311 int x1, int y1, int x2, int y2)
316 if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc
317 || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= t->nc
318 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
319 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
321 printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
322 "in table size (%d,%d)\n"),
323 x1, t->col_ofs, x1 + t->col_ofs,
324 y1, t->row_ofs, y1 + t->row_ofs,
325 x2, t->col_ofs, x2 + t->col_ofs,
326 y2, t->row_ofs, y2 + t->row_ofs,
347 for (x = x1; x <= x2; x++)
349 t->rh[x + t->cf * y1] = f_h;
350 t->rh[x + t->cf * (y2 + 1)] = f_h;
356 for (y = y1; y <= y2; y++)
358 t->rv[x1 + (t->cf + 1) * y] = f_v;
359 t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
367 for (y = y1 + 1; y <= y2; y++)
371 for (x = x1; x <= x2; x++)
372 t->rh[x + t->cf * y] = i_h;
379 for (x = x1 + 1; x <= x2; x++)
383 for (y = y1; y <= y2; y++)
384 t->rv[x + (t->cf + 1) * y] = i_v;
389 /* Formats text TEXT and arguments ARGS as indicated in OPT in
390 TABLE's pool and returns the resultant string. */
391 static struct substring
392 text_format (struct tab_table *table, int opt, const char *text, va_list args)
394 assert (table != NULL && text != NULL);
396 return ss_cstr (opt & TAT_PRINTF
397 ? pool_vasprintf (table->container, text, args)
398 : pool_strdup (table->container, text));
401 /* Set the title of table T to TITLE, which is formatted as if
402 passed to printf(). */
404 tab_title (struct tab_table *t, const char *title, ...)
408 assert (t != NULL && title != NULL);
409 va_start (args, title);
410 t->title = xvasprintf (title, args);
414 /* Set DIM_FUNC, which will be passed auxiliary data AUX, as the
415 dimension function for table T.
417 DIM_FUNC must not assume that it is called from the same
418 context as tab_dim; for example, table T might be kept in
419 memory and, thus, DIM_FUNC might be called after the currently
420 running command completes. If it is non-null, FREE_FUNC is
421 called when the table is destroyed, to allow any data
422 allocated for use by DIM_FUNC to be freed. */
424 tab_dim (struct tab_table *t,
425 tab_dim_func *dim_func, tab_dim_free_func *free_func, void *aux)
427 assert (t->dim == NULL);
429 t->dim_free = free_func;
433 /* Returns the natural width of column C in table T for driver D, that
434 is, the smallest width necessary to display all its cells without
435 wrapping. The width will be no larger than the page width minus
436 left and right rule widths. */
438 tab_natural_width (const struct tab_rendering *r, int col)
440 const struct tab_table *t = r->table;
441 int width, row, max_width;
443 assert (col >= 0 && col < t->nc);
446 for (row = 0; row < t->nr; row++)
448 struct outp_text text;
449 unsigned char opt = t->ct[col + row * t->cf];
452 if (opt & (TAB_JOIN | TAB_EMPTY))
455 text.string = t->cc[col + row * t->cf];
456 text.justification = OUTP_LEFT;
457 text.font = options_to_font (opt);
458 text.h = text.v = INT_MAX;
460 r->driver->class->text_metrics (r->driver, &text, &w, NULL);
467 /* FIXME: This is an ugly kluge to compensate for the fact
468 that we don't let joined cells contribute to column
470 width = r->driver->prop_em_width * 8;
473 max_width = r->driver->width - r->wrv[0] - r->wrv[t->nc];
474 return MIN (width, max_width);
477 /* Returns the natural height of row R in table T for driver D, that
478 is, the minimum height necessary to display the information in the
479 cell at the widths set for each column. */
481 tab_natural_height (const struct tab_rendering *r, int row)
483 const struct tab_table *t = r->table;
486 assert (row >= 0 && row < t->nr);
488 height = r->driver->font_height;
489 for (col = 0; col < t->nc; col++)
491 struct outp_text text;
492 unsigned char opt = t->ct[col + row * t->cf];
495 if (opt & (TAB_JOIN | TAB_EMPTY))
498 text.string = t->cc[col + row * t->cf];
499 text.justification = OUTP_LEFT;
500 text.font = options_to_font (opt);
503 r->driver->class->text_metrics (r->driver, &text, NULL, &h);
512 /* Callback function to set all columns and rows to their natural
513 dimensions. Not really meant to be called directly. */
515 tab_natural_dimensions (struct tab_rendering *r, void *aux UNUSED)
517 const struct tab_table *t = r->table;
520 for (i = 0; i < t->nc; i++)
521 r->w[i] = tab_natural_width (r, i);
523 for (i = 0; i < t->nr; i++)
524 r->h[i] = tab_natural_height (r, i);
530 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
531 from V, displayed with format spec F. */
533 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
534 const union value *v, const struct fmt_spec *f)
538 assert (table != NULL && v != NULL && f != NULL);
540 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
541 || c + table->col_ofs >= table->nc
542 || r + table->row_ofs >= table->nr)
544 printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
546 c, table->col_ofs, c + table->col_ofs,
547 r, table->row_ofs, r + table->row_ofs,
548 table->nc, table->nr);
553 contents = pool_alloc (table->container, f->w);
554 table->cc[c + r * table->cf] = ss_buffer (contents, f->w);
555 table->ct[c + r * table->cf] = opt;
557 data_out (v, f, contents);
560 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
561 with NDEC decimal places. */
563 tab_fixed (struct tab_table *table, int c, int r, unsigned char opt,
564 double val, int w, int d)
570 union value double_value;
572 assert (table != NULL && w <= 40);
575 assert (c < table->nc);
577 assert (r < table->nr);
579 f = fmt_for_output (FMT_F, w, d);
582 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
583 || c + table->col_ofs >= table->nc
584 || r + table->row_ofs >= table->nr)
586 printf ("tab_fixed(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
588 c, table->col_ofs, c + table->col_ofs,
589 r, table->row_ofs, r + table->row_ofs,
590 table->nc, table->nr);
595 double_value.f = val;
596 data_out (&double_value, &f, buf);
599 while (isspace ((unsigned char) *cp) && cp < &buf[w])
601 f.w = w - (cp - buf);
603 contents = pool_alloc (table->container, f.w);
604 table->cc[c + r * table->cf] = ss_buffer (contents, f.w);
605 table->ct[c + r * table->cf] = opt;
606 memcpy (contents, cp, f.w);
609 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL as
611 If FMT is null, then the default print format will be used.
614 tab_double (struct tab_table *table, int c, int r, unsigned char opt,
615 double val, const struct fmt_spec *fmt)
621 union value double_value;
623 assert (table != NULL);
626 assert (c < table->nc);
628 assert (r < table->nr);
631 fmt = settings_get_format ();
633 fmt_check_output (fmt);
636 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
637 || c + table->col_ofs >= table->nc
638 || r + table->row_ofs >= table->nr)
640 printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
642 c, table->col_ofs, c + table->col_ofs,
643 r, table->row_ofs, r + table->row_ofs,
644 table->nc, table->nr);
649 double_value.f = val;
650 data_out (&double_value, fmt, buf);
653 while (isspace ((unsigned char) *cp) && cp < &buf[fmt->w])
655 w = fmt->w - (cp - buf);
657 contents = pool_alloc (table->container, w);
658 table->cc[c + r * table->cf] = ss_buffer (contents, w);
659 table->ct[c + r * table->cf] = opt;
660 memcpy (contents, cp, w);
664 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
667 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
671 assert (table != NULL && text != NULL);
675 assert (c < table->nc);
676 assert (r < table->nr);
680 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
681 || c + table->col_ofs >= table->nc
682 || r + table->row_ofs >= table->nr)
684 printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
686 c, table->col_ofs, c + table->col_ofs,
687 r, table->row_ofs, r + table->row_ofs,
688 table->nc, table->nr);
693 va_start (args, text);
694 table->cc[c + r * table->cf] = text_format (table, opt, text, args);
695 table->ct[c + r * table->cf] = opt;
699 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
700 options OPT to have text value TEXT. */
702 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
703 unsigned opt, const char *text, ...)
705 struct tab_joined_cell *j;
707 assert (table != NULL && text != NULL);
709 assert (x1 + table->col_ofs >= 0);
710 assert (y1 + table->row_ofs >= 0);
713 assert (y2 + table->row_ofs < table->nr);
714 assert (x2 + table->col_ofs < table->nc);
717 if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
718 || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
719 || x2 < x1 || x2 + table->col_ofs >= table->nc
720 || y2 < y2 || y2 + table->row_ofs >= table->nr)
722 printf ("tab_joint_text(): bad cell "
723 "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
724 x1, table->col_ofs, x1 + table->col_ofs,
725 y1, table->row_ofs, y1 + table->row_ofs,
726 x2, table->col_ofs, x2 + table->col_ofs,
727 y2, table->row_ofs, y2 + table->row_ofs,
728 table->nc, table->nr);
733 tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
735 j = pool_alloc (table->container, sizeof *j);
737 j->x1 = x1 + table->col_ofs;
738 j->y1 = y1 + table->row_ofs;
739 j->x2 = ++x2 + table->col_ofs;
740 j->y2 = ++y2 + table->row_ofs;
745 va_start (args, text);
746 j->contents = text_format (table, opt, text, args);
753 struct substring *cc = &table->cc[x1 + y1 * table->cf];
754 unsigned char *ct = &table->ct[x1 + y1 * table->cf];
755 const int ofs = table->cf - (x2 - x1);
759 for (y = y1; y < y2; y++)
763 for (x = x1; x < x2; x++)
765 *cc++ = ss_buffer ((char *) j, 0);
775 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
777 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
778 struct substring *string)
780 assert (table != NULL && string != NULL);
783 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
784 || c + table->col_ofs >= table->nc
785 || r + table->row_ofs >= table->nr)
787 printf ("tab_raw(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
789 c, table->col_ofs, c + table->col_ofs,
790 r, table->row_ofs, r + table->row_ofs,
791 table->nc, table->nr);
796 table->cc[c + r * table->cf] = *string;
797 table->ct[c + r * table->cf] = opt;
802 /* Sets the widths of all the columns and heights of all the rows in
803 table T for driver D. */
805 nowrap_dim (struct tab_rendering *r, void *aux UNUSED)
807 r->w[0] = tab_natural_width (r, 0);
808 r->h[0] = r->driver->font_height;
811 /* Sets the widths of all the columns and heights of all the rows in
812 table T for driver D. */
814 wrap_dim (struct tab_rendering *r, void *aux UNUSED)
816 r->w[0] = tab_natural_width (r, 0);
817 r->h[0] = tab_natural_height (r, 0);
820 /* Outputs text BUF as a table with a single cell having cell options
821 OPTIONS, which is a combination of the TAB_* and TAT_*
824 tab_output_text (int options, const char *buf, ...)
826 struct tab_table *t = tab_create (1, 1, 0);
827 char *tmp_buf = NULL;
829 if (options & TAT_PRINTF)
833 va_start (args, buf);
834 buf = tmp_buf = xvasprintf (buf, args);
838 tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
839 tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
840 tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL, NULL);
846 /* Set table flags to FLAGS. */
848 tab_flags (struct tab_table *t, unsigned flags)
854 /* Easy, type-safe way to submit a tab table to som. */
856 tab_submit (struct tab_table *t)
861 s.class = &tab_table_class;
870 /* Set table row and column offsets for all functions that affect
873 tab_offset (struct tab_table *t, int col, int row)
879 if (row < -1 || row > t->nr)
881 printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
884 if (col < -1 || col > t->nc)
886 printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
892 diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
894 diff += (col - t->col_ofs), t->col_ofs = col;
900 /* Increment the row offset by one. If the table is too small,
901 increase its size. */
903 tab_next_row (struct tab_table *t)
908 if (++t->row_ofs >= t->nr)
909 tab_realloc (t, -1, t->nr * 4 / 3);
912 /* Return the number of columns and rows in the table into N_COLUMNS
913 and N_ROWS, respectively. */
915 tabi_count (struct som_entity *t_, int *n_columns, int *n_rows)
917 struct tab_table *t = t_->ext;
922 /* Return the column style for this table into STYLE. */
924 tabi_columns (struct som_entity *t_, int *style)
926 struct tab_table *t = t_->ext;
927 *style = t->col_style;
930 /* Return the number of header rows/columns on the left, right, top,
931 and bottom sides into HL, HR, HT, and HB, respectively. */
933 tabi_headers (struct som_entity *t_, int *hl, int *hr, int *ht, int *hb)
935 struct tab_table *t = t_->ext;
942 /* Return flags set for the current table into FLAGS. */
944 tabi_flags (struct som_entity *t_, unsigned *flags)
946 struct tab_table *t = t_->ext;
950 /* Returns the line style to use for spacing purposes for a rule
951 of the given TYPE. */
952 static enum outp_line_style
953 rule_to_spacing_type (unsigned char type)
961 return OUTP_L_SINGLE;
963 return OUTP_L_DOUBLE;
970 tabi_render_init (struct som_entity *t_, struct outp_driver *driver,
971 int hl, int hr, int ht, int hb)
973 const struct tab_table *t = t_->ext;
974 struct tab_rendering *r;
978 tab_offset (t_->ext, 0, 0);
980 r = xmalloc (sizeof *r);
983 r->w = xnmalloc (t->nc, sizeof *r->w);
984 r->h = xnmalloc (t->nr, sizeof *r->h);
985 r->hrh = xnmalloc (t->nr + 1, sizeof *r->hrh);
986 r->wrv = xnmalloc (t->nc + 1, sizeof *r->wrv);
992 /* Figure out sizes of rules. */
993 for (row = 0; row <= t->nr; row++)
996 for (col = 0; col < t->nc; col++)
998 unsigned char rh = t->rh[col + row * t->cf];
999 int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
1003 r->hrh[row] = width;
1006 for (col = 0; col <= t->nc; col++)
1009 for (row = 0; row < t->nr; row++)
1011 unsigned char *rv = &t->rv[col + row * (t->cf + 1)];
1013 if (*rv == UCHAR_MAX)
1014 *rv = col != 0 && col != t->nc ? TAL_GAP : TAL_0;
1015 w = driver->vert_line_width[rule_to_spacing_type (*rv)];
1019 r->wrv[col] = width;
1022 /* Determine row heights and columns widths. */
1023 for (i = 0; i < t->nr; i++)
1025 for (i = 0; i < t->nc; i++)
1028 t->dim (r, t->dim_aux);
1030 for (i = 0; i < t->nr; i++)
1032 error (0, 0, "height of table row %d is %d (not initialized?)",
1034 for (i = 0; i < t->nc; i++)
1036 error (0, 0, "width of table column %d is %d (not initialized?)",
1039 /* Add up header sizes. */
1040 for (i = 0, r->wl = r->wrv[0]; i < r->l; i++)
1041 r->wl += r->w[i] + r->wrv[i + 1];
1042 for (i = 0, r->ht = r->hrh[0]; i < r->t; i++)
1043 r->ht += r->h[i] + r->hrh[i + 1];
1044 for (i = t->nc - r->r, r->wr = r->wrv[i]; i < t->nc; i++)
1045 r->wr += r->w[i] + r->wrv[i + 1];
1046 for (i = t->nr - r->b, r->hb = r->hrh[i]; i < t->nr; i++)
1047 r->hb += r->h[i] + r->hrh[i + 1];
1050 if (!(t->flags & SOMF_NO_TITLE))
1051 r->ht += driver->font_height;
1057 tabi_render_free (void *r_)
1059 struct tab_rendering *r = r_;
1068 /* Return the horizontal and vertical size of the entire table,
1069 including headers, for the current output device, into HORIZ and
1072 tabi_area (void *r_, int *horiz, int *vert)
1074 struct tab_rendering *r = r_;
1075 const struct tab_table *t = r->table;
1080 for (col = r->l + 1, width = r->wl + r->wr + r->w[t->l];
1081 col < t->nc - r->r; col++)
1082 width += r->w[col] + r->wrv[col];
1086 for (row = r->t + 1, height = r->ht + r->hb + r->h[t->t];
1087 row < t->nr - t->b; row++)
1088 height += r->h[row] + r->hrh[row];
1092 /* Determines the number of rows or columns (including appropriate
1093 headers), depending on CUMTYPE, that will fit into the space
1094 specified. Takes rows/columns starting at index START and attempts
1095 to fill up available space MAX. Returns in END the index of the
1096 last row/column plus one; returns in ACTUAL the actual amount of
1097 space the selected rows/columns (including appropriate headers)
1100 tabi_cumulate (void *r_, int cumtype, int start, int *end,
1101 int max, int *actual)
1103 const struct tab_rendering *r = r_;
1104 const struct tab_table *t = r->table;
1110 assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1111 if (cumtype == SOM_ROWS)
1113 assert (start >= 0 && start < t->nr);
1114 limit = t->nr - r->b;
1115 cells = &r->h[start];
1116 rules = &r->hrh[start + 1];
1117 total = r->ht + r->hb;
1121 assert (start >= 0 && start < t->nc);
1122 limit = t->nc - t->r;
1123 cells = &r->w[start];
1124 rules = &r->wrv[start + 1];
1125 total = r->wl + r->wr;
1138 for (idx = start + 1; idx < limit; idx++)
1140 int amt = *cells++ + *rules++;
1157 /* Render title for current table, with major index X and minor index
1158 Y. Y may be zero, or X and Y may be zero, but X should be nonzero
1161 tabi_title (void *r_, int x, int y, int table_num, int subtable_num)
1163 const struct tab_rendering *r = r_;
1164 const struct tab_table *t = r->table;
1165 struct outp_text text;
1166 struct string title;
1168 if (t->flags & SOMF_NO_TITLE)
1171 ds_init_empty (&title);
1172 ds_put_format (&title,"%d.%d", table_num, subtable_num);
1174 ds_put_format (&title, "(%d:%d)", x, y);
1176 ds_put_format (&title, "(%d)", x);
1177 if (command_name != NULL)
1178 ds_put_format (&title, " %s", command_name);
1179 ds_put_cstr (&title, ". ");
1180 if (t->title != NULL)
1181 ds_put_cstr (&title, t->title);
1183 text.font = OUTP_PROPORTIONAL;
1184 text.justification = OUTP_LEFT;
1185 text.string = ds_ss (&title);
1186 text.h = r->driver->width;
1187 text.v = r->driver->font_height;
1189 text.y = r->driver->cp_y;
1190 r->driver->class->text_draw (r->driver, &text);
1192 ds_destroy (&title);
1195 static int render_strip (const struct tab_rendering *,
1196 int x, int y, int r, int c1, int c2, int r1, int r2);
1199 add_range (int ranges[][2], int *np, int start, int end)
1202 if (n == 0 || start > ranges[n - 1][1])
1204 ranges[n][0] = start;
1209 ranges[n - 1][1] = end;
1212 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1213 current position on the current output device. */
1215 tabi_render (void *r_, int c0, int r0, int c1, int r1)
1217 const struct tab_rendering *r = r_;
1218 const struct tab_table *t = r->table;
1219 int rows[3][2], cols[3][2];
1220 int n_row_ranges, n_col_ranges;
1223 /* Rows to render, counting horizontal rules as rows. */
1225 add_range (rows, &n_row_ranges, 0, t->t * 2 + 1);
1226 add_range (rows, &n_row_ranges, r0 * 2 + 1, r1 * 2);
1227 add_range (rows, &n_row_ranges, (t->nr - t->b) * 2, t->nr * 2 + 1);
1229 /* Columns to render, counting vertical rules as columns. */
1231 add_range (cols, &n_col_ranges, 0, r->l * 2 + 1);
1232 add_range (cols, &n_col_ranges, c0 * 2 + 1, c1 * 2);
1233 add_range (cols, &n_col_ranges, (t->nc - r->r) * 2, t->nc * 2 + 1);
1235 y = r->driver->cp_y;
1236 if (!(t->flags & SOMF_NO_TITLE))
1237 y += r->driver->font_height;
1238 for (i = 0; i < n_row_ranges; i++)
1242 for (row = rows[i][0]; row < rows[i][1]; row++)
1246 x = r->driver->cp_x;
1247 for (j = 0; j < n_col_ranges; j++)
1248 x = render_strip (r, x, y, row,
1249 cols[j][0], cols[j][1],
1250 rows[i][0], rows[i][1]);
1252 y += (row & 1) ? r->h[row / 2] : r->hrh[row / 2];
1257 const struct som_table_class tab_table_class =
1273 static enum outp_justification
1274 translate_justification (unsigned int opt)
1276 switch (opt & TAB_ALIGN_MASK)
1289 /* Returns the line style to use for drawing a rule of the given
1291 static enum outp_line_style
1292 rule_to_draw_type (unsigned char type)
1300 return OUTP_L_SINGLE;
1302 return OUTP_L_DOUBLE;
1308 /* Returns the horizontal rule at the given column and row. */
1310 get_hrule (const struct tab_table *t, int col, int row)
1312 return t->rh[col + row * t->cf];
1315 /* Returns the vertical rule at the given column and row. */
1317 get_vrule (const struct tab_table *t, int col, int row)
1319 return t->rv[col + row * (t->cf + 1)];
1322 /* Renders the horizontal rule at the given column and row
1323 at (X,Y) on the page. */
1325 render_horz_rule (const struct tab_rendering *r,
1326 int x, int y, int col, int row)
1328 enum outp_line_style style;
1329 style = rule_to_draw_type (get_hrule (r->table, col, row));
1330 if (style != OUTP_L_NONE)
1331 r->driver->class->line (r->driver, x, y, x + r->w[col], y + r->hrh[row],
1332 OUTP_L_NONE, style, OUTP_L_NONE, style);
1335 /* Renders the vertical rule at the given column and row
1336 at (X,Y) on the page. */
1338 render_vert_rule (const struct tab_rendering *r,
1339 int x, int y, int col, int row)
1341 enum outp_line_style style;
1342 style = rule_to_draw_type (get_vrule (r->table, col, row));
1343 if (style != OUTP_L_NONE)
1344 r->driver->class->line (r->driver, x, y, x + r->wrv[col], y + r->h[row],
1345 style, OUTP_L_NONE, style, OUTP_L_NONE);
1348 /* Renders the rule intersection at the given column and row
1349 at (X,Y) on the page. */
1351 render_rule_intersection (const struct tab_rendering *r,
1352 int x, int y, int col, int row)
1354 const struct tab_table *t = r->table;
1356 /* Bounds of intersection. */
1359 int x1 = x + r->wrv[col];
1360 int y1 = y + r->hrh[row];
1362 /* Lines on each side of intersection. */
1363 int top = row > 0 ? get_vrule (t, col, row - 1) : TAL_0;
1364 int left = col > 0 ? get_hrule (t, col - 1, row) : TAL_0;
1365 int bottom = row < t->nr ? get_vrule (t, col, row) : TAL_0;
1366 int right = col < t->nc ? get_hrule (t, col, row) : TAL_0;
1368 /* Output style for each line. */
1369 enum outp_line_style o_top = rule_to_draw_type (top);
1370 enum outp_line_style o_left = rule_to_draw_type (left);
1371 enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1372 enum outp_line_style o_right = rule_to_draw_type (right);
1374 if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1375 || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1376 r->driver->class->line (r->driver, x0, y0, x1, y1,
1377 o_top, o_left, o_bottom, o_right);
1380 /* Returns the width of columns C1...C2 exclusive,
1381 including interior but not exterior rules. */
1383 strip_width (const struct tab_rendering *r, int c1, int c2)
1388 for (c = c1; c < c2; c++)
1389 width += r->w[c] + r->wrv[c + 1];
1391 width -= r->wrv[c2];
1395 /* Returns the height of rows R1...R2 exclusive,
1396 including interior but not exterior rules. */
1398 strip_height (const struct tab_rendering *r, int r1, int r2)
1403 for (row = r1; row < r2; row++)
1404 height += r->h[row] + r->hrh[row + 1];
1406 height -= r->hrh[r2];
1410 /* Renders the cell at the given column and row at (X,Y) on the
1411 page. Also renders joined cells that extend as far to the
1412 right as C1 and as far down as R1. */
1414 render_cell (const struct tab_rendering *r,
1415 int x, int y, int col, int row, int c1, int r1)
1417 const struct tab_table *t = r->table;
1418 const int index = col + (row * t->cf);
1419 unsigned char type = t->ct[index];
1420 struct substring *content = &t->cc[index];
1422 if (!(type & TAB_JOIN))
1424 if (!(type & TAB_EMPTY))
1426 struct outp_text text;
1427 text.font = options_to_font (type);
1428 text.justification = translate_justification (type);
1429 text.string = *content;
1434 r->driver->class->text_draw (r->driver, &text);
1439 struct tab_joined_cell *j
1440 = (struct tab_joined_cell *) ss_data (*content);
1442 if (j->x1 == col && j->y1 == row)
1444 struct outp_text text;
1445 text.font = options_to_font (type);
1446 text.justification = translate_justification (type);
1447 text.string = j->contents;
1450 text.h = strip_width (r, j->x1, MIN (j->x2, c1));
1451 text.v = strip_height (r, j->y1, MIN (j->y2, r1));
1452 r->driver->class->text_draw (r->driver, &text);
1457 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1458 on row ROW, at (X,Y). Returns X position after rendering.
1459 Also renders joined cells that extend beyond that strip,
1460 cropping them to lie within rendering region (C0,R0)-(C1,R1).
1461 C0 and C1 count vertical rules as columns.
1462 ROW counts horizontal rules as rows, but R0 and R1 do not. */
1464 render_strip (const struct tab_rendering *r,
1465 int x, int y, int row, int c0, int c1, int r0 UNUSED, int r1)
1469 for (col = c0; col < c1; col++)
1473 render_cell (r, x, y, col / 2, row / 2, c1 / 2, r1);
1475 render_horz_rule (r, x, y, col / 2, row / 2);
1481 render_vert_rule (r, x, y, col / 2, row / 2);
1483 render_rule_intersection (r, x, y, col / 2, row / 2);
1484 x += r->wrv[col / 2];
1490 /* Sets COMMAND_NAME as the name of the current command,
1491 for embedding in output. */
1493 tab_set_command_name (const char *command_name_)
1495 free (command_name);
1496 command_name = command_name_ ? xstrdup (command_name_) : NULL;