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);
65 t->col_style = TAB_COL_NONE;
71 t->l = t->r = t->t = t->b = 0;
73 t->cc = pool_nmalloc (t->container, nr * nc, sizeof *t->cc);
74 t->ct = pool_malloc (t->container, nr * nc);
75 memset (t->ct, TAB_EMPTY, nc * nr);
77 t->rh = pool_nmalloc (t->container, nc, nr + 1);
78 memset (t->rh, 0, nc * (nr + 1));
80 t->rv = pool_nmalloc (t->container, nr, nc + 1);
81 memset (t->rv, UCHAR_MAX, nr * (nc + 1));
84 t->col_ofs = t->row_ofs = 0;
89 /* Destroys table T. */
91 tab_destroy (struct tab_table *t)
94 if (t->dim_free != NULL)
95 t->dim_free (t->dim_aux);
97 pool_destroy (t->container);
100 /* Sets the width and height of a table, in columns and rows,
101 respectively. Use only to reduce the size of a table, since it
102 does not change the amount of allocated memory. */
104 tab_resize (struct tab_table *t, int nc, int nr)
109 assert (nc + t->col_ofs <= t->cf);
110 t->nc = nc + t->col_ofs;
114 assert (nr + t->row_ofs <= t->nr);
115 t->nr = nr + t->row_ofs;
119 /* Changes either or both dimensions of a table. Consider using the
120 above routine instead if it won't waste a lot of space.
122 Changing the number of columns in a table is particularly expensive
123 in space and time. Avoid doing such. FIXME: In fact, transferring
124 of rules isn't even implemented yet. */
126 tab_realloc (struct tab_table *t, int nc, int nr)
134 tab_offset (t, 0, 0);
141 assert (nc == t->nc);
145 int mr1 = MIN (nr, t->nr);
146 int mc1 = MIN (nc, t->nc);
148 struct substring *new_cc;
149 unsigned char *new_ct;
152 new_cc = pool_nmalloc (t->container, nr * nc, sizeof *new_cc);
153 new_ct = pool_malloc (t->container, nr * nc);
154 for (r = 0; r < mr1; r++)
156 memcpy (&new_cc[r * nc], &t->cc[r * t->nc], mc1 * sizeof *t->cc);
157 memcpy (&new_ct[r * nc], &t->ct[r * t->nc], mc1);
158 memset (&new_ct[r * nc + t->nc], TAB_EMPTY, nc - t->nc);
160 pool_free (t->container, t->cc);
161 pool_free (t->container, t->ct);
166 else if (nr != t->nr)
168 t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc);
169 t->ct = pool_realloc (t->container, t->ct, nr * nc);
171 t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
172 t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
176 memset (&t->rh[nc * (t->nr + 1)], TAL_0, (nr - t->nr) * nc);
177 memset (&t->rv[(nc + 1) * t->nr], UCHAR_MAX,
178 (nr - t->nr) * (nc + 1));
182 memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr));
188 tab_offset (t, co, ro);
191 /* Sets the number of header rows on each side of TABLE to L on the
192 left, R on the right, T on the top, B on the bottom. Header rows
193 are repeated when a table is broken across multiple columns or
196 tab_headers (struct tab_table *table, int l, int r, int t, int b)
198 assert (table != NULL);
199 assert (l < table->nc);
200 assert (r < table->nc);
201 assert (t < table->nr);
202 assert (b < table->nr);
211 /* Set up table T so that, when it is an appropriate size, it will be
212 displayed across the page in columns.
214 STYLE is a TAB_COL_* constant. GROUP is the number of rows to take
217 tab_columns (struct tab_table *t, int style, int group)
220 t->col_style = style;
221 t->col_group = group;
226 /* Draws a vertical line to the left of cells at horizontal position X
227 from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
229 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
234 if (x + t->col_ofs < 0 || x + t->col_ofs > t->nc
235 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
236 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
238 printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
239 "table size (%d,%d)\n"),
240 x, t->col_ofs, x + t->col_ofs,
241 y1, t->row_ofs, y1 + t->row_ofs,
242 y2, t->row_ofs, y2 + t->row_ofs,
256 assert (y2 <= t->nr);
261 for (y = y1; y <= y2; y++)
262 t->rv[x + (t->cf + 1) * y] = style;
266 /* Draws a horizontal line above cells at vertical position Y from X1
267 to X2 inclusive in style STYLE, if style is not -1. */
269 tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
286 for (x = x1; x <= x2; x++)
287 t->rh[x + t->cf * y] = style;
291 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
292 lines of style F_H and vertical lines of style F_V. Fills the
293 interior of the box with horizontal lines of style I_H and vertical
294 lines of style I_V. Any of the line styles may be -1 to avoid
295 drawing those lines. This is distinct from 0, which draws a null
298 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
299 int x1, int y1, int x2, int y2)
304 if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc
305 || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= t->nc
306 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
307 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
309 printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
310 "in table size (%d,%d)\n"),
311 x1, t->col_ofs, x1 + t->col_ofs,
312 y1, t->row_ofs, y1 + t->row_ofs,
313 x2, t->col_ofs, x2 + t->col_ofs,
314 y2, t->row_ofs, y2 + t->row_ofs,
335 for (x = x1; x <= x2; x++)
337 t->rh[x + t->cf * y1] = f_h;
338 t->rh[x + t->cf * (y2 + 1)] = f_h;
344 for (y = y1; y <= y2; y++)
346 t->rv[x1 + (t->cf + 1) * y] = f_v;
347 t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
355 for (y = y1 + 1; y <= y2; y++)
359 for (x = x1; x <= x2; x++)
360 t->rh[x + t->cf * y] = i_h;
367 for (x = x1 + 1; x <= x2; x++)
371 for (y = y1; y <= y2; y++)
372 t->rv[x + (t->cf + 1) * y] = i_v;
377 /* Formats text TEXT and arguments ARGS as indicated in OPT in
378 TABLE's pool and returns the resultant string. */
379 static struct substring
380 text_format (struct tab_table *table, int opt, const char *text, va_list args)
382 assert (table != NULL && text != NULL);
384 return ss_cstr (opt & TAT_PRINTF
385 ? pool_vasprintf (table->container, text, args)
386 : pool_strdup (table->container, text));
389 /* Set the title of table T to TITLE, which is formatted as if
390 passed to printf(). */
392 tab_title (struct tab_table *t, const char *title, ...)
396 assert (t != NULL && title != NULL);
397 va_start (args, title);
398 t->title = xvasprintf (title, args);
402 /* Set DIM_FUNC, which will be passed auxiliary data AUX, as the
403 dimension function for table T.
405 DIM_FUNC must not assume that it is called from the same
406 context as tab_dim; for example, table T might be kept in
407 memory and, thus, DIM_FUNC might be called after the currently
408 running command completes. If it is non-null, FREE_FUNC is
409 called when the table is destroyed, to allow any data
410 allocated for use by DIM_FUNC to be freed. */
412 tab_dim (struct tab_table *t,
413 tab_dim_func *dim_func, tab_dim_free_func *free_func, void *aux)
415 assert (t->dim == NULL);
417 t->dim_free = free_func;
421 /* Returns the natural width of column C in table T for driver D, that
422 is, the smallest width necessary to display all its cells without
423 wrapping. The width will be no larger than the page width minus
424 left and right rule widths. */
426 tab_natural_width (const struct tab_rendering *r, int col)
428 const struct tab_table *t = r->table;
429 int width, row, max_width;
431 assert (col >= 0 && col < t->nc);
434 for (row = 0; row < t->nr; row++)
436 struct outp_text text;
437 unsigned char opt = t->ct[col + row * t->cf];
440 if (opt & (TAB_JOIN | TAB_EMPTY))
443 text.string = t->cc[col + row * t->cf];
444 text.justification = OUTP_LEFT;
445 text.font = options_to_font (opt);
446 text.h = text.v = INT_MAX;
448 r->driver->class->text_metrics (r->driver, &text, &w, NULL);
455 /* FIXME: This is an ugly kluge to compensate for the fact
456 that we don't let joined cells contribute to column
458 width = r->driver->prop_em_width * 8;
461 max_width = r->driver->width - r->wrv[0] - r->wrv[t->nc];
462 return MIN (width, max_width);
465 /* Returns the natural height of row R in table T for driver D, that
466 is, the minimum height necessary to display the information in the
467 cell at the widths set for each column. */
469 tab_natural_height (const struct tab_rendering *r, int row)
471 const struct tab_table *t = r->table;
474 assert (row >= 0 && row < t->nr);
476 height = r->driver->font_height;
477 for (col = 0; col < t->nc; col++)
479 struct outp_text text;
480 unsigned char opt = t->ct[col + row * t->cf];
483 if (opt & (TAB_JOIN | TAB_EMPTY))
486 text.string = t->cc[col + row * t->cf];
487 text.justification = OUTP_LEFT;
488 text.font = options_to_font (opt);
491 r->driver->class->text_metrics (r->driver, &text, NULL, &h);
500 /* Callback function to set all columns and rows to their natural
501 dimensions. Not really meant to be called directly. */
503 tab_natural_dimensions (struct tab_rendering *r, void *aux UNUSED)
505 const struct tab_table *t = r->table;
508 for (i = 0; i < t->nc; i++)
509 r->w[i] = tab_natural_width (r, i);
511 for (i = 0; i < t->nr; i++)
512 r->h[i] = tab_natural_height (r, i);
518 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
519 from V, displayed with format spec F. */
521 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
522 const union value *v, const struct fmt_spec *f)
526 assert (table != NULL && v != NULL && f != NULL);
528 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
529 || c + table->col_ofs >= table->nc
530 || r + table->row_ofs >= table->nr)
532 printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
534 c, table->col_ofs, c + table->col_ofs,
535 r, table->row_ofs, r + table->row_ofs,
536 table->nc, table->nr);
541 contents = pool_alloc (table->container, f->w);
542 table->cc[c + r * table->cf] = ss_buffer (contents, f->w);
543 table->ct[c + r * table->cf] = opt;
545 data_out (v, f, contents);
548 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
549 with NDEC decimal places. */
551 tab_fixed (struct tab_table *table, int c, int r, unsigned char opt,
552 double val, int w, int d)
558 union value double_value;
560 assert (table != NULL && w <= 40);
563 assert (c < table->nc);
565 assert (r < table->nr);
567 f = fmt_for_output (FMT_F, w, d);
570 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
571 || c + table->col_ofs >= table->nc
572 || r + table->row_ofs >= table->nr)
574 printf ("tab_fixed(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
576 c, table->col_ofs, c + table->col_ofs,
577 r, table->row_ofs, r + table->row_ofs,
578 table->nc, table->nr);
583 double_value.f = val;
584 data_out (&double_value, &f, buf);
587 while (isspace ((unsigned char) *cp) && cp < &buf[w])
589 f.w = w - (cp - buf);
591 contents = pool_alloc (table->container, f.w);
592 table->cc[c + r * table->cf] = ss_buffer (contents, f.w);
593 table->ct[c + r * table->cf] = opt;
594 memcpy (contents, cp, f.w);
597 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL as
599 If FMT is null, then the default print format will be used.
602 tab_double (struct tab_table *table, int c, int r, unsigned char opt,
603 double val, const struct fmt_spec *fmt)
609 union value double_value;
611 assert (table != NULL);
614 assert (c < table->nc);
616 assert (r < table->nr);
619 fmt = settings_get_format ();
621 fmt_check_output (fmt);
624 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
625 || c + table->col_ofs >= table->nc
626 || r + table->row_ofs >= table->nr)
628 printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
630 c, table->col_ofs, c + table->col_ofs,
631 r, table->row_ofs, r + table->row_ofs,
632 table->nc, table->nr);
637 double_value.f = val;
638 data_out (&double_value, fmt, buf);
641 while (isspace ((unsigned char) *cp) && cp < &buf[fmt->w])
643 w = fmt->w - (cp - buf);
645 contents = pool_alloc (table->container, w);
646 table->cc[c + r * table->cf] = ss_buffer (contents, w);
647 table->ct[c + r * table->cf] = opt;
648 memcpy (contents, cp, w);
652 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
655 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
659 assert (table != NULL && text != NULL);
663 assert (c < table->nc);
664 assert (r < table->nr);
668 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
669 || c + table->col_ofs >= table->nc
670 || r + table->row_ofs >= table->nr)
672 printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
674 c, table->col_ofs, c + table->col_ofs,
675 r, table->row_ofs, r + table->row_ofs,
676 table->nc, table->nr);
681 va_start (args, text);
682 table->cc[c + r * table->cf] = text_format (table, opt, text, args);
683 table->ct[c + r * table->cf] = opt;
687 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
688 options OPT to have text value TEXT. */
690 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
691 unsigned opt, const char *text, ...)
693 struct tab_joined_cell *j;
695 assert (table != NULL && text != NULL);
697 assert (x1 + table->col_ofs >= 0);
698 assert (y1 + table->row_ofs >= 0);
701 assert (y2 + table->row_ofs < table->nr);
702 assert (x2 + table->col_ofs < table->nc);
705 if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
706 || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
707 || x2 < x1 || x2 + table->col_ofs >= table->nc
708 || y2 < y2 || y2 + table->row_ofs >= table->nr)
710 printf ("tab_joint_text(): bad cell "
711 "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
712 x1, table->col_ofs, x1 + table->col_ofs,
713 y1, table->row_ofs, y1 + table->row_ofs,
714 x2, table->col_ofs, x2 + table->col_ofs,
715 y2, table->row_ofs, y2 + table->row_ofs,
716 table->nc, table->nr);
721 tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
723 j = pool_alloc (table->container, sizeof *j);
725 j->x1 = x1 + table->col_ofs;
726 j->y1 = y1 + table->row_ofs;
727 j->x2 = ++x2 + table->col_ofs;
728 j->y2 = ++y2 + table->row_ofs;
733 va_start (args, text);
734 j->contents = text_format (table, opt, text, args);
741 struct substring *cc = &table->cc[x1 + y1 * table->cf];
742 unsigned char *ct = &table->ct[x1 + y1 * table->cf];
743 const int ofs = table->cf - (x2 - x1);
747 for (y = y1; y < y2; y++)
751 for (x = x1; x < x2; x++)
753 *cc++ = ss_buffer ((char *) j, 0);
763 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
765 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
766 struct substring *string)
768 assert (table != NULL && string != NULL);
771 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
772 || c + table->col_ofs >= table->nc
773 || r + table->row_ofs >= table->nr)
775 printf ("tab_raw(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
777 c, table->col_ofs, c + table->col_ofs,
778 r, table->row_ofs, r + table->row_ofs,
779 table->nc, table->nr);
784 table->cc[c + r * table->cf] = *string;
785 table->ct[c + r * table->cf] = opt;
790 /* Sets the widths of all the columns and heights of all the rows in
791 table T for driver D. */
793 nowrap_dim (struct tab_rendering *r, void *aux UNUSED)
795 r->w[0] = tab_natural_width (r, 0);
796 r->h[0] = r->driver->font_height;
799 /* Sets the widths of all the columns and heights of all the rows in
800 table T for driver D. */
802 wrap_dim (struct tab_rendering *r, void *aux UNUSED)
804 r->w[0] = tab_natural_width (r, 0);
805 r->h[0] = tab_natural_height (r, 0);
808 /* Outputs text BUF as a table with a single cell having cell options
809 OPTIONS, which is a combination of the TAB_* and TAT_*
812 tab_output_text (int options, const char *buf, ...)
814 struct tab_table *t = tab_create (1, 1, 0);
815 char *tmp_buf = NULL;
817 if (options & TAT_PRINTF)
821 va_start (args, buf);
822 buf = tmp_buf = xvasprintf (buf, args);
826 tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
827 tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
828 tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL, NULL);
834 /* Set table flags to FLAGS. */
836 tab_flags (struct tab_table *t, unsigned flags)
842 /* Easy, type-safe way to submit a tab table to som. */
844 tab_submit (struct tab_table *t)
849 s.class = &tab_table_class;
858 /* Set table row and column offsets for all functions that affect
861 tab_offset (struct tab_table *t, int col, int row)
867 if (row < -1 || row > t->nr)
869 printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
872 if (col < -1 || col > t->nc)
874 printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
880 diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
882 diff += (col - t->col_ofs), t->col_ofs = col;
888 /* Increment the row offset by one. If the table is too small,
889 increase its size. */
891 tab_next_row (struct tab_table *t)
896 if (++t->row_ofs >= t->nr)
897 tab_realloc (t, -1, t->nr * 4 / 3);
900 /* Return the number of columns and rows in the table into N_COLUMNS
901 and N_ROWS, respectively. */
903 tabi_count (struct som_entity *t_, int *n_columns, int *n_rows)
905 struct tab_table *t = t_->ext;
910 /* Return the column style for this table into STYLE. */
912 tabi_columns (struct som_entity *t_, int *style)
914 struct tab_table *t = t_->ext;
915 *style = t->col_style;
918 /* Return the number of header rows/columns on the left, right, top,
919 and bottom sides into HL, HR, HT, and HB, respectively. */
921 tabi_headers (struct som_entity *t_, int *hl, int *hr, int *ht, int *hb)
923 struct tab_table *t = t_->ext;
930 /* Return flags set for the current table into FLAGS. */
932 tabi_flags (struct som_entity *t_, unsigned *flags)
934 struct tab_table *t = t_->ext;
938 /* Returns the line style to use for spacing purposes for a rule
939 of the given TYPE. */
940 static enum outp_line_style
941 rule_to_spacing_type (unsigned char type)
949 return OUTP_L_SINGLE;
951 return OUTP_L_DOUBLE;
958 tabi_render_init (struct som_entity *t_, struct outp_driver *driver,
959 int hl, int hr, int ht, int hb)
961 const struct tab_table *t = t_->ext;
962 struct tab_rendering *r;
966 tab_offset (t_->ext, 0, 0);
968 r = xmalloc (sizeof *r);
971 r->w = xnmalloc (t->nc, sizeof *r->w);
972 r->h = xnmalloc (t->nr, sizeof *r->h);
973 r->hrh = xnmalloc (t->nr + 1, sizeof *r->hrh);
974 r->wrv = xnmalloc (t->nc + 1, sizeof *r->wrv);
980 /* Figure out sizes of rules. */
981 for (row = 0; row <= t->nr; row++)
984 for (col = 0; col < t->nc; col++)
986 unsigned char rh = t->rh[col + row * t->cf];
987 int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
994 for (col = 0; col <= t->nc; col++)
997 for (row = 0; row < t->nr; row++)
999 unsigned char *rv = &t->rv[col + row * (t->cf + 1)];
1001 if (*rv == UCHAR_MAX)
1002 *rv = col != 0 && col != t->nc ? TAL_GAP : TAL_0;
1003 w = driver->vert_line_width[rule_to_spacing_type (*rv)];
1007 r->wrv[col] = width;
1010 /* Determine row heights and columns widths. */
1011 for (i = 0; i < t->nr; i++)
1013 for (i = 0; i < t->nc; i++)
1016 t->dim (r, t->dim_aux);
1018 for (i = 0; i < t->nr; i++)
1020 error (0, 0, "height of table row %d is %d (not initialized?)",
1022 for (i = 0; i < t->nc; i++)
1024 error (0, 0, "width of table column %d is %d (not initialized?)",
1027 /* Add up header sizes. */
1028 for (i = 0, r->wl = r->wrv[0]; i < r->l; i++)
1029 r->wl += r->w[i] + r->wrv[i + 1];
1030 for (i = 0, r->ht = r->hrh[0]; i < r->t; i++)
1031 r->ht += r->h[i] + r->hrh[i + 1];
1032 for (i = t->nc - r->r, r->wr = r->wrv[i]; i < t->nc; i++)
1033 r->wr += r->w[i] + r->wrv[i + 1];
1034 for (i = t->nr - r->b, r->hb = r->hrh[i]; i < t->nr; i++)
1035 r->hb += r->h[i] + r->hrh[i + 1];
1038 if (!(t->flags & SOMF_NO_TITLE))
1039 r->ht += driver->font_height;
1045 tabi_render_free (void *r_)
1047 struct tab_rendering *r = r_;
1056 /* Return the horizontal and vertical size of the entire table,
1057 including headers, for the current output device, into HORIZ and
1060 tabi_area (void *r_, int *horiz, int *vert)
1062 struct tab_rendering *r = r_;
1063 const struct tab_table *t = r->table;
1068 for (col = r->l + 1, width = r->wl + r->wr + r->w[t->l];
1069 col < t->nc - r->r; col++)
1070 width += r->w[col] + r->wrv[col];
1074 for (row = r->t + 1, height = r->ht + r->hb + r->h[t->t];
1075 row < t->nr - t->b; row++)
1076 height += r->h[row] + r->hrh[row];
1080 /* Determines the number of rows or columns (including appropriate
1081 headers), depending on CUMTYPE, that will fit into the space
1082 specified. Takes rows/columns starting at index START and attempts
1083 to fill up available space MAX. Returns in END the index of the
1084 last row/column plus one; returns in ACTUAL the actual amount of
1085 space the selected rows/columns (including appropriate headers)
1088 tabi_cumulate (void *r_, int cumtype, int start, int *end,
1089 int max, int *actual)
1091 const struct tab_rendering *r = r_;
1092 const struct tab_table *t = r->table;
1098 assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1099 if (cumtype == SOM_ROWS)
1101 assert (start >= 0 && start < t->nr);
1102 limit = t->nr - r->b;
1103 cells = &r->h[start];
1104 rules = &r->hrh[start + 1];
1105 total = r->ht + r->hb;
1109 assert (start >= 0 && start < t->nc);
1110 limit = t->nc - t->r;
1111 cells = &r->w[start];
1112 rules = &r->wrv[start + 1];
1113 total = r->wl + r->wr;
1126 for (idx = start + 1; idx < limit; idx++)
1128 int amt = *cells++ + *rules++;
1145 /* Render title for current table, with major index X and minor index
1146 Y. Y may be zero, or X and Y may be zero, but X should be nonzero
1149 tabi_title (void *r_, int x, int y, int table_num, int subtable_num)
1151 const struct tab_rendering *r = r_;
1152 const struct tab_table *t = r->table;
1153 struct outp_text text;
1154 struct string title;
1156 if (t->flags & SOMF_NO_TITLE)
1159 ds_init_empty (&title);
1160 ds_put_format (&title,"%d.%d", table_num, subtable_num);
1162 ds_put_format (&title, "(%d:%d)", x, y);
1164 ds_put_format (&title, "(%d)", x);
1165 if (command_name != NULL)
1166 ds_put_format (&title, " %s", command_name);
1167 ds_put_cstr (&title, ". ");
1168 if (t->title != NULL)
1169 ds_put_cstr (&title, t->title);
1171 text.font = OUTP_PROPORTIONAL;
1172 text.justification = OUTP_LEFT;
1173 text.string = ds_ss (&title);
1174 text.h = r->driver->width;
1175 text.v = r->driver->font_height;
1177 text.y = r->driver->cp_y;
1178 r->driver->class->text_draw (r->driver, &text);
1180 ds_destroy (&title);
1183 static int render_strip (const struct tab_rendering *,
1184 int x, int y, int r, int c1, int c2, int r1, int r2);
1187 add_range (int ranges[][2], int *np, int start, int end)
1190 if (n == 0 || start > ranges[n - 1][1])
1192 ranges[n][0] = start;
1197 ranges[n - 1][1] = end;
1200 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1201 current position on the current output device. */
1203 tabi_render (void *r_, int c0, int r0, int c1, int r1)
1205 const struct tab_rendering *r = r_;
1206 const struct tab_table *t = r->table;
1207 int rows[3][2], cols[3][2];
1208 int n_row_ranges, n_col_ranges;
1211 /* Rows to render, counting horizontal rules as rows. */
1213 add_range (rows, &n_row_ranges, 0, t->t * 2 + 1);
1214 add_range (rows, &n_row_ranges, r0 * 2 + 1, r1 * 2);
1215 add_range (rows, &n_row_ranges, (t->nr - t->b) * 2, t->nr * 2 + 1);
1217 /* Columns to render, counting vertical rules as columns. */
1219 add_range (cols, &n_col_ranges, 0, r->l * 2 + 1);
1220 add_range (cols, &n_col_ranges, c0 * 2 + 1, c1 * 2);
1221 add_range (cols, &n_col_ranges, (t->nc - r->r) * 2, t->nc * 2 + 1);
1223 y = r->driver->cp_y;
1224 if (!(t->flags & SOMF_NO_TITLE))
1225 y += r->driver->font_height;
1226 for (i = 0; i < n_row_ranges; i++)
1230 for (row = rows[i][0]; row < rows[i][1]; row++)
1234 x = r->driver->cp_x;
1235 for (j = 0; j < n_col_ranges; j++)
1236 x = render_strip (r, x, y, row,
1237 cols[j][0], cols[j][1],
1238 rows[i][0], rows[i][1]);
1240 y += (row & 1) ? r->h[row / 2] : r->hrh[row / 2];
1245 const struct som_table_class tab_table_class =
1261 static enum outp_justification
1262 translate_justification (unsigned int opt)
1264 switch (opt & TAB_ALIGN_MASK)
1277 /* Returns the line style to use for drawing a rule of the given
1279 static enum outp_line_style
1280 rule_to_draw_type (unsigned char type)
1288 return OUTP_L_SINGLE;
1290 return OUTP_L_DOUBLE;
1296 /* Returns the horizontal rule at the given column and row. */
1298 get_hrule (const struct tab_table *t, int col, int row)
1300 return t->rh[col + row * t->cf];
1303 /* Returns the vertical rule at the given column and row. */
1305 get_vrule (const struct tab_table *t, int col, int row)
1307 return t->rv[col + row * (t->cf + 1)];
1310 /* Renders the horizontal rule at the given column and row
1311 at (X,Y) on the page. */
1313 render_horz_rule (const struct tab_rendering *r,
1314 int x, int y, int col, int row)
1316 enum outp_line_style style;
1317 style = rule_to_draw_type (get_hrule (r->table, col, row));
1318 if (style != OUTP_L_NONE)
1319 r->driver->class->line (r->driver, x, y, x + r->w[col], y + r->hrh[row],
1320 OUTP_L_NONE, style, OUTP_L_NONE, style);
1323 /* Renders the vertical rule at the given column and row
1324 at (X,Y) on the page. */
1326 render_vert_rule (const struct tab_rendering *r,
1327 int x, int y, int col, int row)
1329 enum outp_line_style style;
1330 style = rule_to_draw_type (get_vrule (r->table, col, row));
1331 if (style != OUTP_L_NONE)
1332 r->driver->class->line (r->driver, x, y, x + r->wrv[col], y + r->h[row],
1333 style, OUTP_L_NONE, style, OUTP_L_NONE);
1336 /* Renders the rule intersection at the given column and row
1337 at (X,Y) on the page. */
1339 render_rule_intersection (const struct tab_rendering *r,
1340 int x, int y, int col, int row)
1342 const struct tab_table *t = r->table;
1344 /* Bounds of intersection. */
1347 int x1 = x + r->wrv[col];
1348 int y1 = y + r->hrh[row];
1350 /* Lines on each side of intersection. */
1351 int top = row > 0 ? get_vrule (t, col, row - 1) : TAL_0;
1352 int left = col > 0 ? get_hrule (t, col - 1, row) : TAL_0;
1353 int bottom = row < t->nr ? get_vrule (t, col, row) : TAL_0;
1354 int right = col < t->nc ? get_hrule (t, col, row) : TAL_0;
1356 /* Output style for each line. */
1357 enum outp_line_style o_top = rule_to_draw_type (top);
1358 enum outp_line_style o_left = rule_to_draw_type (left);
1359 enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1360 enum outp_line_style o_right = rule_to_draw_type (right);
1362 if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1363 || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1364 r->driver->class->line (r->driver, x0, y0, x1, y1,
1365 o_top, o_left, o_bottom, o_right);
1368 /* Returns the width of columns C1...C2 exclusive,
1369 including interior but not exterior rules. */
1371 strip_width (const struct tab_rendering *r, int c1, int c2)
1376 for (c = c1; c < c2; c++)
1377 width += r->w[c] + r->wrv[c + 1];
1379 width -= r->wrv[c2];
1383 /* Returns the height of rows R1...R2 exclusive,
1384 including interior but not exterior rules. */
1386 strip_height (const struct tab_rendering *r, int r1, int r2)
1391 for (row = r1; row < r2; row++)
1392 height += r->h[row] + r->hrh[row + 1];
1394 height -= r->hrh[r2];
1398 /* Renders the cell at the given column and row at (X,Y) on the
1399 page. Also renders joined cells that extend as far to the
1400 right as C1 and as far down as R1. */
1402 render_cell (const struct tab_rendering *r,
1403 int x, int y, int col, int row, int c1, int r1)
1405 const struct tab_table *t = r->table;
1406 const int index = col + (row * t->cf);
1407 unsigned char type = t->ct[index];
1408 struct substring *content = &t->cc[index];
1410 if (!(type & TAB_JOIN))
1412 if (!(type & TAB_EMPTY))
1414 struct outp_text text;
1415 text.font = options_to_font (type);
1416 text.justification = translate_justification (type);
1417 text.string = *content;
1422 r->driver->class->text_draw (r->driver, &text);
1427 struct tab_joined_cell *j
1428 = (struct tab_joined_cell *) ss_data (*content);
1430 if (j->x1 == col && j->y1 == row)
1432 struct outp_text text;
1433 text.font = options_to_font (type);
1434 text.justification = translate_justification (type);
1435 text.string = j->contents;
1438 text.h = strip_width (r, j->x1, MIN (j->x2, c1));
1439 text.v = strip_height (r, j->y1, MIN (j->y2, r1));
1440 r->driver->class->text_draw (r->driver, &text);
1445 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1446 on row ROW, at (X,Y). Returns X position after rendering.
1447 Also renders joined cells that extend beyond that strip,
1448 cropping them to lie within rendering region (C0,R0)-(C1,R1).
1449 C0 and C1 count vertical rules as columns.
1450 ROW counts horizontal rules as rows, but R0 and R1 do not. */
1452 render_strip (const struct tab_rendering *r,
1453 int x, int y, int row, int c0, int c1, int r0 UNUSED, int r1)
1457 for (col = c0; col < c1; col++)
1461 render_cell (r, x, y, col / 2, row / 2, c1 / 2, r1);
1463 render_horz_rule (r, x, y, col / 2, row / 2);
1469 render_vert_rule (r, x, y, col / 2, row / 2);
1471 render_rule_intersection (r, x, y, col / 2, row / 2);
1472 x += r->wrv[col / 2];
1478 /* Sets COMMAND_NAME as the name of the current command,
1479 for embedding in output. */
1481 tab_set_command_name (const char *command_name_)
1483 free (command_name);
1484 command_name = command_name_ ? xstrdup (command_name_) : NULL;