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)
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 (const struct tab_rendering *r, int col)
416 const struct tab_table *t = r->table;
417 int width, row, max_width;
419 assert (col >= 0 && col < t->nc);
422 for (row = 0; row < t->nr; row++)
424 struct outp_text text;
425 unsigned char opt = t->ct[col + row * t->cf];
428 if (opt & (TAB_JOIN | TAB_EMPTY))
431 text.string = t->cc[col + row * t->cf];
432 text.justification = OUTP_LEFT;
433 text.font = options_to_font (opt);
434 text.h = text.v = INT_MAX;
436 r->driver->class->text_metrics (r->driver, &text, &w, NULL);
443 /* FIXME: This is an ugly kluge to compensate for the fact
444 that we don't let joined cells contribute to column
446 width = r->driver->prop_em_width * 8;
449 max_width = r->driver->width - r->wrv[0] - r->wrv[t->nc];
450 return MIN (width, max_width);
453 /* Returns the natural height of row R in table T for driver D, that
454 is, the minimum height necessary to display the information in the
455 cell at the widths set for each column. */
457 tab_natural_height (const struct tab_rendering *r, int row)
459 const struct tab_table *t = r->table;
462 assert (row >= 0 && row < t->nr);
464 height = r->driver->font_height;
465 for (col = 0; col < t->nc; col++)
467 struct outp_text text;
468 unsigned char opt = t->ct[col + row * t->cf];
471 if (opt & (TAB_JOIN | TAB_EMPTY))
474 text.string = t->cc[col + row * t->cf];
475 text.justification = OUTP_LEFT;
476 text.font = options_to_font (opt);
479 r->driver->class->text_metrics (r->driver, &text, NULL, &h);
488 /* Callback function to set all columns and rows to their natural
489 dimensions. Not really meant to be called directly. */
491 tab_natural_dimensions (struct tab_rendering *r, void *aux UNUSED)
493 const struct tab_table *t = r->table;
496 for (i = 0; i < t->nc; i++)
497 r->w[i] = tab_natural_width (r, i);
499 for (i = 0; i < t->nr; i++)
500 r->h[i] = tab_natural_height (r, i);
506 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
507 from V, displayed with format spec F. */
509 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
510 const union value *v, const struct fmt_spec *f)
514 assert (table != NULL && v != NULL && f != NULL);
516 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
517 || c + table->col_ofs >= table->nc
518 || r + table->row_ofs >= table->nr)
520 printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
522 c, table->col_ofs, c + table->col_ofs,
523 r, table->row_ofs, r + table->row_ofs,
524 table->nc, table->nr);
529 contents = pool_alloc (table->container, f->w);
530 table->cc[c + r * table->cf] = ss_buffer (contents, f->w);
531 table->ct[c + r * table->cf] = opt;
533 data_out (v, f, contents);
536 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
537 with NDEC decimal places. */
539 tab_fixed (struct tab_table *table, int c, int r, unsigned char opt,
540 double val, int w, int d)
546 union value double_value;
548 assert (table != NULL && w <= 40);
551 assert (c < table->nc);
553 assert (r < table->nr);
555 f = fmt_for_output (FMT_F, w, d);
558 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
559 || c + table->col_ofs >= table->nc
560 || r + table->row_ofs >= table->nr)
562 printf ("tab_fixed(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
564 c, table->col_ofs, c + table->col_ofs,
565 r, table->row_ofs, r + table->row_ofs,
566 table->nc, table->nr);
571 double_value.f = val;
572 data_out (&double_value, &f, buf);
575 while (isspace ((unsigned char) *cp) && cp < &buf[w])
577 f.w = w - (cp - buf);
579 contents = pool_alloc (table->container, f.w);
580 table->cc[c + r * table->cf] = ss_buffer (contents, f.w);
581 table->ct[c + r * table->cf] = opt;
582 memcpy (contents, cp, f.w);
585 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL as
587 If FMT is null, then the default print format will be used.
590 tab_double (struct tab_table *table, int c, int r, unsigned char opt,
591 double val, const struct fmt_spec *fmt)
597 union value double_value;
599 assert (table != NULL);
602 assert (c < table->nc);
604 assert (r < table->nr);
607 fmt = settings_get_format ();
609 fmt_check_output (fmt);
612 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
613 || c + table->col_ofs >= table->nc
614 || r + table->row_ofs >= table->nr)
616 printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
618 c, table->col_ofs, c + table->col_ofs,
619 r, table->row_ofs, r + table->row_ofs,
620 table->nc, table->nr);
625 double_value.f = val;
626 data_out (&double_value, fmt, buf);
629 while (isspace ((unsigned char) *cp) && cp < &buf[fmt->w])
631 w = fmt->w - (cp - buf);
633 contents = pool_alloc (table->container, w);
634 table->cc[c + r * table->cf] = ss_buffer (contents, w);
635 table->ct[c + r * table->cf] = opt;
636 memcpy (contents, cp, w);
640 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
643 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
647 assert (table != NULL && text != NULL);
651 assert (c < table->nc);
652 assert (r < table->nr);
656 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
657 || c + table->col_ofs >= table->nc
658 || r + table->row_ofs >= table->nr)
660 printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
662 c, table->col_ofs, c + table->col_ofs,
663 r, table->row_ofs, r + table->row_ofs,
664 table->nc, table->nr);
669 va_start (args, text);
670 table->cc[c + r * table->cf] = text_format (table, opt, text, args);
671 table->ct[c + r * table->cf] = opt;
675 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
676 options OPT to have text value TEXT. */
678 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
679 unsigned opt, const char *text, ...)
681 struct tab_joined_cell *j;
683 assert (table != NULL && text != NULL);
685 assert (x1 + table->col_ofs >= 0);
686 assert (y1 + table->row_ofs >= 0);
689 assert (y2 + table->row_ofs < table->nr);
690 assert (x2 + table->col_ofs < table->nc);
693 if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
694 || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
695 || x2 < x1 || x2 + table->col_ofs >= table->nc
696 || y2 < y2 || y2 + table->row_ofs >= table->nr)
698 printf ("tab_joint_text(): bad cell "
699 "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
700 x1, table->col_ofs, x1 + table->col_ofs,
701 y1, table->row_ofs, y1 + table->row_ofs,
702 x2, table->col_ofs, x2 + table->col_ofs,
703 y2, table->row_ofs, y2 + table->row_ofs,
704 table->nc, table->nr);
709 tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
711 j = pool_alloc (table->container, sizeof *j);
713 j->x1 = x1 + table->col_ofs;
714 j->y1 = y1 + table->row_ofs;
715 j->x2 = ++x2 + table->col_ofs;
716 j->y2 = ++y2 + table->row_ofs;
721 va_start (args, text);
722 j->contents = text_format (table, opt, text, args);
729 struct substring *cc = &table->cc[x1 + y1 * table->cf];
730 unsigned char *ct = &table->ct[x1 + y1 * table->cf];
731 const int ofs = table->cf - (x2 - x1);
735 for (y = y1; y < y2; y++)
739 for (x = x1; x < x2; x++)
741 *cc++ = ss_buffer ((char *) j, 0);
751 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
753 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
754 struct substring *string)
756 assert (table != NULL && string != NULL);
759 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
760 || c + table->col_ofs >= table->nc
761 || r + table->row_ofs >= table->nr)
763 printf ("tab_raw(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
765 c, table->col_ofs, c + table->col_ofs,
766 r, table->row_ofs, r + table->row_ofs,
767 table->nc, table->nr);
772 table->cc[c + r * table->cf] = *string;
773 table->ct[c + r * table->cf] = opt;
778 /* Sets the widths of all the columns and heights of all the rows in
779 table T for driver D. */
781 nowrap_dim (struct tab_rendering *r, void *aux UNUSED)
783 r->w[0] = tab_natural_width (r, 0);
784 r->h[0] = r->driver->font_height;
787 /* Sets the widths of all the columns and heights of all the rows in
788 table T for driver D. */
790 wrap_dim (struct tab_rendering *r, void *aux UNUSED)
792 r->w[0] = tab_natural_width (r, 0);
793 r->h[0] = tab_natural_height (r, 0);
796 /* Outputs text BUF as a table with a single cell having cell options
797 OPTIONS, which is a combination of the TAB_* and TAT_*
800 tab_output_text (int options, const char *buf, ...)
802 struct tab_table *t = tab_create (1, 1, 0);
803 char *tmp_buf = NULL;
805 if (options & TAT_PRINTF)
809 va_start (args, buf);
810 buf = tmp_buf = xvasprintf (buf, args);
814 tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
815 tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
816 tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL);
822 /* Set table flags to FLAGS. */
824 tab_flags (struct tab_table *t, unsigned flags)
830 /* Easy, type-safe way to submit a tab table to som. */
832 tab_submit (struct tab_table *t)
837 s.class = &tab_table_class;
846 /* Set table row and column offsets for all functions that affect
849 tab_offset (struct tab_table *t, int col, int row)
855 if (row < -1 || row > t->nr)
857 printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
860 if (col < -1 || col > t->nc)
862 printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
868 diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
870 diff += (col - t->col_ofs), t->col_ofs = col;
876 /* Increment the row offset by one. If the table is too small,
877 increase its size. */
879 tab_next_row (struct tab_table *t)
884 if (++t->row_ofs >= t->nr)
885 tab_realloc (t, -1, t->nr * 4 / 3);
888 /* Return the number of columns and rows in the table into N_COLUMNS
889 and N_ROWS, respectively. */
891 tabi_count (struct som_entity *t_, int *n_columns, int *n_rows)
893 struct tab_table *t = t_->ext;
898 /* Return the column style for this table into STYLE. */
900 tabi_columns (struct som_entity *t_, int *style)
902 struct tab_table *t = t_->ext;
903 *style = t->col_style;
906 /* Return the number of header rows/columns on the left, right, top,
907 and bottom sides into HL, HR, HT, and HB, respectively. */
909 tabi_headers (struct som_entity *t_, int *hl, int *hr, int *ht, int *hb)
911 struct tab_table *t = t_->ext;
918 /* Return flags set for the current table into FLAGS. */
920 tabi_flags (struct som_entity *t_, unsigned *flags)
922 struct tab_table *t = t_->ext;
926 /* Returns the line style to use for spacing purposes for a rule
927 of the given TYPE. */
928 static enum outp_line_style
929 rule_to_spacing_type (unsigned char type)
937 return OUTP_L_SINGLE;
939 return OUTP_L_DOUBLE;
946 tabi_render_init (struct som_entity *t_, struct outp_driver *driver,
947 int hl, int hr, int ht, int hb)
949 const struct tab_table *t = t_->ext;
950 struct tab_rendering *r;
954 tab_offset (t_->ext, 0, 0);
956 r = xmalloc (sizeof *r);
959 r->w = xnmalloc (t->nc, sizeof *r->w);
960 r->h = xnmalloc (t->nr, sizeof *r->h);
961 r->hrh = xnmalloc (t->nr + 1, sizeof *r->hrh);
962 r->wrv = xnmalloc (t->nc + 1, sizeof *r->wrv);
968 /* Figure out sizes of rules. */
969 for (row = 0; row <= t->nr; row++)
972 for (col = 0; col < t->nc; col++)
974 unsigned char rh = t->rh[col + row * t->cf];
975 int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
982 for (col = 0; col <= t->nc; col++)
985 for (row = 0; row < t->nr; row++)
987 unsigned char *rv = &t->rv[col + row * (t->cf + 1)];
989 if (*rv == UCHAR_MAX)
990 *rv = col != 0 && col != t->nc ? TAL_GAP : TAL_0;
991 w = driver->vert_line_width[rule_to_spacing_type (*rv)];
998 /* Determine row heights and columns widths. */
999 for (i = 0; i < t->nr; i++)
1001 for (i = 0; i < t->nc; i++)
1004 t->dim (r, t->dim_aux);
1006 for (i = 0; i < t->nr; i++)
1008 error (0, 0, "height of table row %d not initialized", i);
1009 for (i = 0; i < t->nc; i++)
1011 error (0, 0, "width of table column %d not initialized", i);
1013 /* Add up header sizes. */
1014 for (i = 0, r->wl = r->wrv[0]; i < r->l; i++)
1015 r->wl += r->w[i] + r->wrv[i + 1];
1016 for (i = 0, r->ht = r->hrh[0]; i < r->t; i++)
1017 r->ht += r->h[i] + r->hrh[i + 1];
1018 for (i = t->nc - r->r, r->wr = r->wrv[i]; i < t->nc; i++)
1019 r->wr += r->w[i] + r->wrv[i + 1];
1020 for (i = t->nr - r->b, r->hb = r->hrh[i]; i < t->nr; i++)
1021 r->hb += r->h[i] + r->hrh[i + 1];
1024 if (!(t->flags & SOMF_NO_TITLE))
1025 r->ht += driver->font_height;
1031 tabi_render_free (void *r_)
1033 struct tab_rendering *r = r_;
1042 /* Return the horizontal and vertical size of the entire table,
1043 including headers, for the current output device, into HORIZ and
1046 tabi_area (void *r_, int *horiz, int *vert)
1048 struct tab_rendering *r = r_;
1049 const struct tab_table *t = r->table;
1054 for (col = r->l + 1, width = r->wl + r->wr + r->w[t->l];
1055 col < t->nc - r->r; col++)
1056 width += r->w[col] + r->wrv[col];
1060 for (row = r->t + 1, height = r->ht + r->hb + r->h[t->t];
1061 row < t->nr - t->b; row++)
1062 height += r->h[row] + r->hrh[row];
1066 /* Determines the number of rows or columns (including appropriate
1067 headers), depending on CUMTYPE, that will fit into the space
1068 specified. Takes rows/columns starting at index START and attempts
1069 to fill up available space MAX. Returns in END the index of the
1070 last row/column plus one; returns in ACTUAL the actual amount of
1071 space the selected rows/columns (including appropriate headers)
1074 tabi_cumulate (void *r_, int cumtype, int start, int *end,
1075 int max, int *actual)
1077 const struct tab_rendering *r = r_;
1078 const struct tab_table *t = r->table;
1084 assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1085 if (cumtype == SOM_ROWS)
1087 assert (start >= 0 && start < t->nr);
1088 limit = t->nr - r->b;
1089 cells = &r->h[start];
1090 rules = &r->hrh[start + 1];
1091 total = r->ht + r->hb;
1095 assert (start >= 0 && start < t->nc);
1096 limit = t->nc - t->r;
1097 cells = &r->w[start];
1098 rules = &r->wrv[start + 1];
1099 total = r->wl + r->wr;
1112 for (idx = start + 1; idx < limit; idx++)
1114 int amt = *cells++ + *rules++;
1131 /* Render title for current table, with major index X and minor index
1132 Y. Y may be zero, or X and Y may be zero, but X should be nonzero
1135 tabi_title (void *r_, int x, int y)
1137 const struct tab_rendering *r = r_;
1138 const struct tab_table *t = r->table;
1139 struct outp_text text;
1140 struct string title;
1142 if (t->flags & SOMF_NO_TITLE)
1145 ds_init_empty (&title);
1146 ds_put_format (&title,"%d.%d", table_num, subtable_num);
1148 ds_put_format (&title, "(%d:%d)", x, y);
1150 ds_put_format (&title, "(%d)", x);
1151 if (command_name != NULL)
1152 ds_put_format (&title, " %s", command_name);
1153 ds_put_cstr (&title, ". ");
1154 if (t->title != NULL)
1155 ds_put_cstr (&title, t->title);
1157 text.font = OUTP_PROPORTIONAL;
1158 text.justification = OUTP_LEFT;
1159 text.string = ds_ss (&title);
1160 text.h = r->driver->width;
1161 text.v = r->driver->font_height;
1163 text.y = r->driver->cp_y;
1164 r->driver->class->text_draw (r->driver, &text);
1166 ds_destroy (&title);
1169 static int render_strip (const struct tab_rendering *,
1170 int x, int y, int r, int c1, int c2, int r1, int r2);
1173 add_range (int ranges[][2], int *np, int start, int end)
1176 if (n == 0 || start > ranges[n - 1][1])
1178 ranges[n][0] = start;
1183 ranges[n - 1][1] = end;
1186 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1187 current position on the current output device. */
1189 tabi_render (void *r_, int c0, int r0, int c1, int r1)
1191 const struct tab_rendering *r = r_;
1192 const struct tab_table *t = r->table;
1193 int rows[3][2], cols[3][2];
1194 int n_row_ranges, n_col_ranges;
1197 /* Rows to render, counting horizontal rules as rows. */
1199 add_range (rows, &n_row_ranges, 0, t->t * 2 + 1);
1200 add_range (rows, &n_row_ranges, r0 * 2 + 1, r1 * 2);
1201 add_range (rows, &n_row_ranges, (t->nr - t->b) * 2, t->nr * 2 + 1);
1203 /* Columns to render, counting vertical rules as columns. */
1205 add_range (cols, &n_col_ranges, 0, r->l * 2 + 1);
1206 add_range (cols, &n_col_ranges, c0 * 2 + 1, c1 * 2);
1207 add_range (cols, &n_col_ranges, (t->nc - r->r) * 2, t->nc * 2 + 1);
1209 y = r->driver->cp_y;
1210 if (!(t->flags & SOMF_NO_TITLE))
1211 y += r->driver->font_height;
1212 for (i = 0; i < n_row_ranges; i++)
1216 for (row = rows[i][0]; row < rows[i][1]; row++)
1220 x = r->driver->cp_x;
1221 for (j = 0; j < n_col_ranges; j++)
1222 x = render_strip (r, x, y, row,
1223 cols[j][0], cols[j][1],
1224 rows[i][0], rows[i][1]);
1226 y += (row & 1) ? r->h[row / 2] : r->hrh[row / 2];
1231 const struct som_table_class tab_table_class =
1247 static enum outp_justification
1248 translate_justification (unsigned int opt)
1250 switch (opt & TAB_ALIGN_MASK)
1263 /* Returns the line style to use for drawing a rule of the given
1265 static enum outp_line_style
1266 rule_to_draw_type (unsigned char type)
1274 return OUTP_L_SINGLE;
1276 return OUTP_L_DOUBLE;
1282 /* Returns the horizontal rule at the given column and row. */
1284 get_hrule (const struct tab_table *t, int col, int row)
1286 return t->rh[col + row * t->cf];
1289 /* Returns the vertical rule at the given column and row. */
1291 get_vrule (const struct tab_table *t, int col, int row)
1293 return t->rv[col + row * (t->cf + 1)];
1296 /* Renders the horizontal rule at the given column and row
1297 at (X,Y) on the page. */
1299 render_horz_rule (const struct tab_rendering *r,
1300 int x, int y, int col, int row)
1302 enum outp_line_style style;
1303 style = rule_to_draw_type (get_hrule (r->table, col, row));
1304 if (style != OUTP_L_NONE)
1305 r->driver->class->line (r->driver, x, y, x + r->w[col], y + r->hrh[row],
1306 OUTP_L_NONE, style, OUTP_L_NONE, style);
1309 /* Renders the vertical rule at the given column and row
1310 at (X,Y) on the page. */
1312 render_vert_rule (const struct tab_rendering *r,
1313 int x, int y, int col, int row)
1315 enum outp_line_style style;
1316 style = rule_to_draw_type (get_vrule (r->table, col, row));
1317 if (style != OUTP_L_NONE)
1318 r->driver->class->line (r->driver, x, y, x + r->wrv[col], y + r->h[row],
1319 style, OUTP_L_NONE, style, OUTP_L_NONE);
1322 /* Renders the rule intersection at the given column and row
1323 at (X,Y) on the page. */
1325 render_rule_intersection (const struct tab_rendering *r,
1326 int x, int y, int col, int row)
1328 const struct tab_table *t = r->table;
1330 /* Bounds of intersection. */
1333 int x1 = x + r->wrv[col];
1334 int y1 = y + r->hrh[row];
1336 /* Lines on each side of intersection. */
1337 int top = row > 0 ? get_vrule (t, col, row - 1) : TAL_0;
1338 int left = col > 0 ? get_hrule (t, col - 1, row) : TAL_0;
1339 int bottom = row < t->nr ? get_vrule (t, col, row) : TAL_0;
1340 int right = col < t->nc ? get_hrule (t, col, row) : TAL_0;
1342 /* Output style for each line. */
1343 enum outp_line_style o_top = rule_to_draw_type (top);
1344 enum outp_line_style o_left = rule_to_draw_type (left);
1345 enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1346 enum outp_line_style o_right = rule_to_draw_type (right);
1348 if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1349 || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1350 r->driver->class->line (r->driver, x0, y0, x1, y1,
1351 o_top, o_left, o_bottom, o_right);
1354 /* Returns the width of columns C1...C2 exclusive,
1355 including interior but not exterior rules. */
1357 strip_width (const struct tab_rendering *r, int c1, int c2)
1362 for (c = c1; c < c2; c++)
1363 width += r->w[c] + r->wrv[c + 1];
1365 width -= r->wrv[c2];
1369 /* Returns the height of rows R1...R2 exclusive,
1370 including interior but not exterior rules. */
1372 strip_height (const struct tab_rendering *r, int r1, int r2)
1377 for (row = r1; row < r2; row++)
1378 height += r->h[row] + r->hrh[row + 1];
1380 height -= r->hrh[r2];
1384 /* Renders the cell at the given column and row at (X,Y) on the
1385 page. Also renders joined cells that extend as far to the
1386 right as C1 and as far down as R1. */
1388 render_cell (const struct tab_rendering *r,
1389 int x, int y, int col, int row, int c1, int r1)
1391 const struct tab_table *t = r->table;
1392 const int index = col + (row * t->cf);
1393 unsigned char type = t->ct[index];
1394 struct substring *content = &t->cc[index];
1396 if (!(type & TAB_JOIN))
1398 if (!(type & TAB_EMPTY))
1400 struct outp_text text;
1401 text.font = options_to_font (type);
1402 text.justification = translate_justification (type);
1403 text.string = *content;
1408 r->driver->class->text_draw (r->driver, &text);
1413 struct tab_joined_cell *j
1414 = (struct tab_joined_cell *) ss_data (*content);
1416 if (j->x1 == col && j->y1 == row)
1418 struct outp_text text;
1419 text.font = options_to_font (type);
1420 text.justification = translate_justification (type);
1421 text.string = j->contents;
1424 text.h = strip_width (r, j->x1, MIN (j->x2, c1));
1425 text.v = strip_height (r, j->y1, MIN (j->y2, r1));
1426 r->driver->class->text_draw (r->driver, &text);
1431 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1432 on row ROW, at (X,Y). Returns X position after rendering.
1433 Also renders joined cells that extend beyond that strip,
1434 cropping them to lie within rendering region (C0,R0)-(C1,R1).
1435 C0 and C1 count vertical rules as columns.
1436 ROW counts horizontal rules as rows, but R0 and R1 do not. */
1438 render_strip (const struct tab_rendering *r,
1439 int x, int y, int row, int c0, int c1, int r0 UNUSED, int r1)
1443 for (col = c0; col < c1; col++)
1447 render_cell (r, x, y, col / 2, row / 2, c1 / 2, r1);
1449 render_horz_rule (r, x, y, col / 2, row / 2);
1455 render_vert_rule (r, x, y, col / 2, row / 2);
1457 render_rule_intersection (r, x, y, col / 2, row / 2);
1458 x += r->wrv[col / 2];
1464 /* Sets COMMAND_NAME as the name of the current command,
1465 for embedding in output. */
1467 tab_set_command_name (const char *command_name_)
1469 free (command_name);
1470 command_name = command_name_ ? xstrdup (command_name_) : NULL;