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;
48 /* Returns the font to use for a cell with the given OPTIONS. */
50 options_to_font (unsigned options)
52 return (options & TAB_FIX ? OUTP_FIXED
53 : options & TAB_EMPH ? OUTP_EMPHASIS
57 /* Creates a table with NC columns and NR rows. */
59 tab_create (int nc, int nr, int reallocable UNUSED)
63 t = pool_create_container (struct tab_table, container);
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 /* Increases T's reference count and, if this causes T's
90 reference count to reach 0, destroys T. */
92 tab_destroy (struct tab_table *t)
94 assert (t->ref_cnt > 0);
97 if (t->dim_free != NULL)
98 t->dim_free (t->dim_aux);
100 pool_destroy (t->container);
103 /* Increases T's reference count. */
105 tab_ref (struct tab_table *t)
107 assert (t->ref_cnt > 0);
111 /* Sets the width and height of a table, in columns and rows,
112 respectively. Use only to reduce the size of a table, since it
113 does not change the amount of allocated memory. */
115 tab_resize (struct tab_table *t, int nc, int nr)
120 assert (nc + t->col_ofs <= t->cf);
121 t->nc = nc + t->col_ofs;
125 assert (nr + t->row_ofs <= t->nr);
126 t->nr = nr + t->row_ofs;
130 /* Changes either or both dimensions of a table. Consider using the
131 above routine instead if it won't waste a lot of space.
133 Changing the number of columns in a table is particularly expensive
134 in space and time. Avoid doing such. FIXME: In fact, transferring
135 of rules isn't even implemented yet. */
137 tab_realloc (struct tab_table *t, int nc, int nr)
145 tab_offset (t, 0, 0);
152 assert (nc == t->nc);
156 int mr1 = MIN (nr, t->nr);
157 int mc1 = MIN (nc, t->nc);
159 struct substring *new_cc;
160 unsigned char *new_ct;
163 new_cc = pool_nmalloc (t->container, nr * nc, sizeof *new_cc);
164 new_ct = pool_malloc (t->container, nr * nc);
165 for (r = 0; r < mr1; r++)
167 memcpy (&new_cc[r * nc], &t->cc[r * t->nc], mc1 * sizeof *t->cc);
168 memcpy (&new_ct[r * nc], &t->ct[r * t->nc], mc1);
169 memset (&new_ct[r * nc + t->nc], TAB_EMPTY, nc - t->nc);
171 pool_free (t->container, t->cc);
172 pool_free (t->container, t->ct);
177 else if (nr != t->nr)
179 t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc);
180 t->ct = pool_realloc (t->container, t->ct, nr * nc);
182 t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
183 t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
187 memset (&t->rh[nc * (t->nr + 1)], TAL_0, (nr - t->nr) * nc);
188 memset (&t->rv[(nc + 1) * t->nr], UCHAR_MAX,
189 (nr - t->nr) * (nc + 1));
193 memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr));
199 tab_offset (t, co, ro);
202 /* Sets the number of header rows on each side of TABLE to L on the
203 left, R on the right, T on the top, B on the bottom. Header rows
204 are repeated when a table is broken across multiple columns or
207 tab_headers (struct tab_table *table, int l, int r, int t, int b)
209 assert (table != NULL);
210 assert (l < table->nc);
211 assert (r < table->nc);
212 assert (t < table->nr);
213 assert (b < table->nr);
222 /* Set up table T so that, when it is an appropriate size, it will be
223 displayed across the page in columns.
225 STYLE is a TAB_COL_* constant. GROUP is the number of rows to take
228 tab_columns (struct tab_table *t, int style, int group)
231 t->col_style = style;
232 t->col_group = group;
237 /* Draws a vertical line to the left of cells at horizontal position X
238 from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
240 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
245 if (x + t->col_ofs < 0 || x + t->col_ofs > t->nc
246 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
247 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
249 printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
250 "table size (%d,%d)\n"),
251 x, t->col_ofs, x + t->col_ofs,
252 y1, t->row_ofs, y1 + t->row_ofs,
253 y2, t->row_ofs, y2 + t->row_ofs,
267 assert (y2 <= t->nr);
272 for (y = y1; y <= y2; y++)
273 t->rv[x + (t->cf + 1) * y] = style;
277 /* Draws a horizontal line above cells at vertical position Y from X1
278 to X2 inclusive in style STYLE, if style is not -1. */
280 tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
297 for (x = x1; x <= x2; x++)
298 t->rh[x + t->cf * y] = style;
302 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
303 lines of style F_H and vertical lines of style F_V. Fills the
304 interior of the box with horizontal lines of style I_H and vertical
305 lines of style I_V. Any of the line styles may be -1 to avoid
306 drawing those lines. This is distinct from 0, which draws a null
309 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
310 int x1, int y1, int x2, int y2)
315 if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc
316 || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= t->nc
317 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
318 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
320 printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
321 "in table size (%d,%d)\n"),
322 x1, t->col_ofs, x1 + t->col_ofs,
323 y1, t->row_ofs, y1 + t->row_ofs,
324 x2, t->col_ofs, x2 + t->col_ofs,
325 y2, t->row_ofs, y2 + t->row_ofs,
346 for (x = x1; x <= x2; x++)
348 t->rh[x + t->cf * y1] = f_h;
349 t->rh[x + t->cf * (y2 + 1)] = f_h;
355 for (y = y1; y <= y2; y++)
357 t->rv[x1 + (t->cf + 1) * y] = f_v;
358 t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
366 for (y = y1 + 1; y <= y2; y++)
370 for (x = x1; x <= x2; x++)
371 t->rh[x + t->cf * y] = i_h;
378 for (x = x1 + 1; x <= x2; x++)
382 for (y = y1; y <= y2; y++)
383 t->rv[x + (t->cf + 1) * y] = i_v;
388 /* Formats text TEXT and arguments ARGS as indicated in OPT in
389 TABLE's pool and returns the resultant string. */
390 static struct substring
391 text_format (struct tab_table *table, int opt, const char *text, va_list args)
393 assert (table != NULL && text != NULL);
395 return ss_cstr (opt & TAT_PRINTF
396 ? pool_vasprintf (table->container, text, args)
397 : pool_strdup (table->container, text));
400 /* Set the title of table T to TITLE, which is formatted as if
401 passed to printf(). */
403 tab_title (struct tab_table *t, const char *title, ...)
407 assert (t != NULL && title != NULL);
408 va_start (args, title);
409 t->title = xvasprintf (title, args);
413 /* Set DIM_FUNC, which will be passed auxiliary data AUX, as the
414 dimension function for table T.
416 DIM_FUNC must not assume that it is called from the same
417 context as tab_dim; for example, table T might be kept in
418 memory and, thus, DIM_FUNC might be called after the currently
419 running command completes. If it is non-null, FREE_FUNC is
420 called when the table is destroyed, to allow any data
421 allocated for use by DIM_FUNC to be freed. */
423 tab_dim (struct tab_table *t,
424 tab_dim_func *dim_func, tab_dim_free_func *free_func, void *aux)
426 assert (t->dim == NULL);
428 t->dim_free = free_func;
432 /* Returns the natural width of column C in table T for driver D, that
433 is, the smallest width necessary to display all its cells without
434 wrapping. The width will be no larger than the page width minus
435 left and right rule widths. */
437 tab_natural_width (const struct tab_rendering *r, int col)
439 const struct tab_table *t = r->table;
440 int width, row, max_width;
442 assert (col >= 0 && col < t->nc);
445 for (row = 0; row < t->nr; row++)
447 struct outp_text text;
448 unsigned char opt = t->ct[col + row * t->cf];
451 if (opt & (TAB_JOIN | TAB_EMPTY))
454 text.string = t->cc[col + row * t->cf];
455 text.justification = OUTP_LEFT;
456 text.font = options_to_font (opt);
457 text.h = text.v = INT_MAX;
459 r->driver->class->text_metrics (r->driver, &text, &w, NULL);
466 /* FIXME: This is an ugly kluge to compensate for the fact
467 that we don't let joined cells contribute to column
469 width = r->driver->prop_em_width * 8;
472 max_width = r->driver->width - r->wrv[0] - r->wrv[t->nc];
473 return MIN (width, max_width);
476 /* Returns the natural height of row R in table T for driver D, that
477 is, the minimum height necessary to display the information in the
478 cell at the widths set for each column. */
480 tab_natural_height (const struct tab_rendering *r, int row)
482 const struct tab_table *t = r->table;
485 assert (row >= 0 && row < t->nr);
487 height = r->driver->font_height;
488 for (col = 0; col < t->nc; col++)
490 struct outp_text text;
491 unsigned char opt = t->ct[col + row * t->cf];
494 if (opt & (TAB_JOIN | TAB_EMPTY))
497 text.string = t->cc[col + row * t->cf];
498 text.justification = OUTP_LEFT;
499 text.font = options_to_font (opt);
502 r->driver->class->text_metrics (r->driver, &text, NULL, &h);
511 /* Callback function to set all columns and rows to their natural
512 dimensions. Not really meant to be called directly. */
514 tab_natural_dimensions (struct tab_rendering *r, void *aux UNUSED)
516 const struct tab_table *t = r->table;
519 for (i = 0; i < t->nc; i++)
520 r->w[i] = tab_natural_width (r, i);
522 for (i = 0; i < t->nr; i++)
523 r->h[i] = tab_natural_height (r, i);
529 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
530 from V, displayed with format spec F. */
532 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
533 const union value *v, const struct fmt_spec *f)
537 assert (table != NULL && v != NULL && f != NULL);
539 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
540 || c + table->col_ofs >= table->nc
541 || r + table->row_ofs >= table->nr)
543 printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
545 c, table->col_ofs, c + table->col_ofs,
546 r, table->row_ofs, r + table->row_ofs,
547 table->nc, table->nr);
552 contents = pool_alloc (table->container, f->w);
553 table->cc[c + r * table->cf] = ss_buffer (contents, f->w);
554 table->ct[c + r * table->cf] = opt;
556 data_out (v, f, contents);
559 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
560 with NDEC decimal places. */
562 tab_fixed (struct tab_table *table, int c, int r, unsigned char opt,
563 double val, int w, int d)
569 union value double_value;
571 assert (table != NULL && w <= 40);
574 assert (c < table->nc);
576 assert (r < table->nr);
578 f = fmt_for_output (FMT_F, w, d);
581 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
582 || c + table->col_ofs >= table->nc
583 || r + table->row_ofs >= table->nr)
585 printf ("tab_fixed(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
587 c, table->col_ofs, c + table->col_ofs,
588 r, table->row_ofs, r + table->row_ofs,
589 table->nc, table->nr);
594 double_value.f = val;
595 data_out (&double_value, &f, buf);
598 while (isspace ((unsigned char) *cp) && cp < &buf[w])
600 f.w = w - (cp - buf);
602 contents = pool_alloc (table->container, f.w);
603 table->cc[c + r * table->cf] = ss_buffer (contents, f.w);
604 table->ct[c + r * table->cf] = opt;
605 memcpy (contents, cp, f.w);
608 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL as
610 If FMT is null, then the default print format will be used.
613 tab_double (struct tab_table *table, int c, int r, unsigned char opt,
614 double val, const struct fmt_spec *fmt)
620 union value double_value;
622 assert (table != NULL);
625 assert (c < table->nc);
627 assert (r < table->nr);
630 fmt = settings_get_format ();
632 fmt_check_output (fmt);
635 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
636 || c + table->col_ofs >= table->nc
637 || r + table->row_ofs >= table->nr)
639 printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
641 c, table->col_ofs, c + table->col_ofs,
642 r, table->row_ofs, r + table->row_ofs,
643 table->nc, table->nr);
648 double_value.f = val;
649 data_out (&double_value, fmt, buf);
652 while (isspace ((unsigned char) *cp) && cp < &buf[fmt->w])
654 w = fmt->w - (cp - buf);
656 contents = pool_alloc (table->container, w);
657 table->cc[c + r * table->cf] = ss_buffer (contents, w);
658 table->ct[c + r * table->cf] = opt;
659 memcpy (contents, cp, w);
663 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
666 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
670 assert (table != NULL && text != NULL);
674 assert (c < table->nc);
675 assert (r < table->nr);
679 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
680 || c + table->col_ofs >= table->nc
681 || r + table->row_ofs >= table->nr)
683 printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
685 c, table->col_ofs, c + table->col_ofs,
686 r, table->row_ofs, r + table->row_ofs,
687 table->nc, table->nr);
692 va_start (args, text);
693 table->cc[c + r * table->cf] = text_format (table, opt, text, args);
694 table->ct[c + r * table->cf] = opt;
698 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
699 options OPT to have text value TEXT. */
701 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
702 unsigned opt, const char *text, ...)
704 struct tab_joined_cell *j;
706 assert (table != NULL && text != NULL);
708 assert (x1 + table->col_ofs >= 0);
709 assert (y1 + table->row_ofs >= 0);
712 assert (y2 + table->row_ofs < table->nr);
713 assert (x2 + table->col_ofs < table->nc);
716 if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
717 || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
718 || x2 < x1 || x2 + table->col_ofs >= table->nc
719 || y2 < y2 || y2 + table->row_ofs >= table->nr)
721 printf ("tab_joint_text(): bad cell "
722 "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
723 x1, table->col_ofs, x1 + table->col_ofs,
724 y1, table->row_ofs, y1 + table->row_ofs,
725 x2, table->col_ofs, x2 + table->col_ofs,
726 y2, table->row_ofs, y2 + table->row_ofs,
727 table->nc, table->nr);
732 tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
734 j = pool_alloc (table->container, sizeof *j);
735 j->x1 = x1 + table->col_ofs;
736 j->y1 = y1 + table->row_ofs;
737 j->x2 = ++x2 + table->col_ofs;
738 j->y2 = ++y2 + table->row_ofs;
743 va_start (args, text);
744 j->contents = text_format (table, opt, text, args);
751 struct substring *cc = &table->cc[x1 + y1 * table->cf];
752 unsigned char *ct = &table->ct[x1 + y1 * table->cf];
753 const int ofs = table->cf - (x2 - x1);
757 for (y = y1; y < y2; y++)
761 for (x = x1; x < x2; x++)
763 *cc++ = ss_buffer ((char *) j, 0);
773 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
775 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
776 struct substring *string)
778 assert (table != NULL && string != NULL);
781 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
782 || c + table->col_ofs >= table->nc
783 || r + table->row_ofs >= table->nr)
785 printf ("tab_raw(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
787 c, table->col_ofs, c + table->col_ofs,
788 r, table->row_ofs, r + table->row_ofs,
789 table->nc, table->nr);
794 table->cc[c + r * table->cf] = *string;
795 table->ct[c + r * table->cf] = opt;
800 /* Sets the widths of all the columns and heights of all the rows in
801 table T for driver D. */
803 nowrap_dim (struct tab_rendering *r, void *aux UNUSED)
805 r->w[0] = tab_natural_width (r, 0);
806 r->h[0] = r->driver->font_height;
809 /* Sets the widths of all the columns and heights of all the rows in
810 table T for driver D. */
812 wrap_dim (struct tab_rendering *r, void *aux UNUSED)
814 r->w[0] = tab_natural_width (r, 0);
815 r->h[0] = tab_natural_height (r, 0);
818 /* Outputs text BUF as a table with a single cell having cell options
819 OPTIONS, which is a combination of the TAB_* and TAT_*
822 tab_output_text (int options, const char *buf, ...)
824 struct tab_table *t = tab_create (1, 1, 0);
825 char *tmp_buf = NULL;
827 if (options & TAT_PRINTF)
831 va_start (args, buf);
832 buf = tmp_buf = xvasprintf (buf, args);
836 tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
837 tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
838 tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL, NULL);
844 /* Set table flags to FLAGS. */
846 tab_flags (struct tab_table *t, unsigned flags)
852 /* Easy, type-safe way to submit a tab table to som. */
854 tab_submit (struct tab_table *t)
859 s.class = &tab_table_class;
868 /* Set table row and column offsets for all functions that affect
871 tab_offset (struct tab_table *t, int col, int row)
877 if (row < -1 || row > t->nr)
879 printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
882 if (col < -1 || col > t->nc)
884 printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
890 diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
892 diff += (col - t->col_ofs), t->col_ofs = col;
898 /* Increment the row offset by one. If the table is too small,
899 increase its size. */
901 tab_next_row (struct tab_table *t)
906 if (++t->row_ofs >= t->nr)
907 tab_realloc (t, -1, t->nr * 4 / 3);
910 /* Return the number of columns and rows in the table into N_COLUMNS
911 and N_ROWS, respectively. */
913 tabi_count (struct som_entity *t_, int *n_columns, int *n_rows)
915 struct tab_table *t = t_->ext;
920 /* Return the column style for this table into STYLE. */
922 tabi_columns (struct som_entity *t_, int *style)
924 struct tab_table *t = t_->ext;
925 *style = t->col_style;
928 /* Return the number of header rows/columns on the left, right, top,
929 and bottom sides into HL, HR, HT, and HB, respectively. */
931 tabi_headers (struct som_entity *t_, int *hl, int *hr, int *ht, int *hb)
933 struct tab_table *t = t_->ext;
940 /* Return flags set for the current table into FLAGS. */
942 tabi_flags (struct som_entity *t_, unsigned *flags)
944 struct tab_table *t = t_->ext;
948 /* Returns the line style to use for spacing purposes for a rule
949 of the given TYPE. */
950 static enum outp_line_style
951 rule_to_spacing_type (unsigned char type)
959 return OUTP_L_SINGLE;
961 return OUTP_L_DOUBLE;
968 tabi_render_init (struct som_entity *t_, struct outp_driver *driver,
969 int hl, int hr, int ht, int hb)
971 const struct tab_table *t = t_->ext;
972 struct tab_rendering *r;
976 tab_offset (t_->ext, 0, 0);
978 r = xmalloc (sizeof *r);
981 r->w = xnmalloc (t->nc, sizeof *r->w);
982 r->h = xnmalloc (t->nr, sizeof *r->h);
983 r->hrh = xnmalloc (t->nr + 1, sizeof *r->hrh);
984 r->wrv = xnmalloc (t->nc + 1, sizeof *r->wrv);
990 /* Figure out sizes of rules. */
991 for (row = 0; row <= t->nr; row++)
994 for (col = 0; col < t->nc; col++)
996 unsigned char rh = t->rh[col + row * t->cf];
997 int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
1001 r->hrh[row] = width;
1004 for (col = 0; col <= t->nc; col++)
1007 for (row = 0; row < t->nr; row++)
1009 unsigned char *rv = &t->rv[col + row * (t->cf + 1)];
1011 if (*rv == UCHAR_MAX)
1012 *rv = col != 0 && col != t->nc ? TAL_GAP : TAL_0;
1013 w = driver->vert_line_width[rule_to_spacing_type (*rv)];
1017 r->wrv[col] = width;
1020 /* Determine row heights and columns widths. */
1021 for (i = 0; i < t->nr; i++)
1023 for (i = 0; i < t->nc; i++)
1026 t->dim (r, t->dim_aux);
1028 for (i = 0; i < t->nr; i++)
1030 error (0, 0, "height of table row %d is %d (not initialized?)",
1032 for (i = 0; i < t->nc; i++)
1034 error (0, 0, "width of table column %d is %d (not initialized?)",
1037 /* Add up header sizes. */
1038 for (i = 0, r->wl = r->wrv[0]; i < r->l; i++)
1039 r->wl += r->w[i] + r->wrv[i + 1];
1040 for (i = 0, r->ht = r->hrh[0]; i < r->t; i++)
1041 r->ht += r->h[i] + r->hrh[i + 1];
1042 for (i = t->nc - r->r, r->wr = r->wrv[i]; i < t->nc; i++)
1043 r->wr += r->w[i] + r->wrv[i + 1];
1044 for (i = t->nr - r->b, r->hb = r->hrh[i]; i < t->nr; i++)
1045 r->hb += r->h[i] + r->hrh[i + 1];
1048 if (!(t->flags & SOMF_NO_TITLE))
1049 r->ht += driver->font_height;
1055 tabi_render_free (void *r_)
1057 struct tab_rendering *r = r_;
1066 /* Return the horizontal and vertical size of the entire table,
1067 including headers, for the current output device, into HORIZ and
1070 tabi_area (void *r_, int *horiz, int *vert)
1072 struct tab_rendering *r = r_;
1073 const struct tab_table *t = r->table;
1078 for (col = r->l + 1, width = r->wl + r->wr + r->w[t->l];
1079 col < t->nc - r->r; col++)
1080 width += r->w[col] + r->wrv[col];
1084 for (row = r->t + 1, height = r->ht + r->hb + r->h[t->t];
1085 row < t->nr - t->b; row++)
1086 height += r->h[row] + r->hrh[row];
1090 /* Determines the number of rows or columns (including appropriate
1091 headers), depending on CUMTYPE, that will fit into the space
1092 specified. Takes rows/columns starting at index START and attempts
1093 to fill up available space MAX. Returns in END the index of the
1094 last row/column plus one; returns in ACTUAL the actual amount of
1095 space the selected rows/columns (including appropriate headers)
1098 tabi_cumulate (void *r_, int cumtype, int start, int *end,
1099 int max, int *actual)
1101 const struct tab_rendering *r = r_;
1102 const struct tab_table *t = r->table;
1108 assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1109 if (cumtype == SOM_ROWS)
1111 assert (start >= 0 && start < t->nr);
1112 limit = t->nr - r->b;
1113 cells = &r->h[start];
1114 rules = &r->hrh[start + 1];
1115 total = r->ht + r->hb;
1119 assert (start >= 0 && start < t->nc);
1120 limit = t->nc - t->r;
1121 cells = &r->w[start];
1122 rules = &r->wrv[start + 1];
1123 total = r->wl + r->wr;
1136 for (idx = start + 1; idx < limit; idx++)
1138 int amt = *cells++ + *rules++;
1155 /* Render title for current table, with major index X and minor index
1156 Y. Y may be zero, or X and Y may be zero, but X should be nonzero
1159 tabi_title (void *r_, int x, int y, int table_num, int subtable_num,
1160 const char *command_name)
1162 const struct tab_rendering *r = r_;
1163 const struct tab_table *t = r->table;
1164 struct outp_text text;
1165 struct string title;
1167 if (t->flags & SOMF_NO_TITLE)
1170 ds_init_empty (&title);
1171 ds_put_format (&title,"%d.%d", table_num, subtable_num);
1173 ds_put_format (&title, "(%d:%d)", x, y);
1175 ds_put_format (&title, "(%d)", x);
1176 if (command_name != NULL)
1177 ds_put_format (&title, " %s", command_name);
1178 ds_put_cstr (&title, ". ");
1179 if (t->title != NULL)
1180 ds_put_cstr (&title, t->title);
1182 text.font = OUTP_PROPORTIONAL;
1183 text.justification = OUTP_LEFT;
1184 text.string = ds_ss (&title);
1185 text.h = r->driver->width;
1186 text.v = r->driver->font_height;
1188 text.y = r->driver->cp_y;
1189 r->driver->class->text_draw (r->driver, &text);
1191 ds_destroy (&title);
1194 static int render_strip (const struct tab_rendering *,
1195 int x, int y, int r, int c1, int c2, int r1, int r2);
1198 add_range (int ranges[][2], int *np, int start, int end)
1201 if (n == 0 || start > ranges[n - 1][1])
1203 ranges[n][0] = start;
1208 ranges[n - 1][1] = end;
1211 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1212 current position on the current output device. */
1214 tabi_render (void *r_, int c0, int r0, int c1, int r1)
1216 const struct tab_rendering *r = r_;
1217 const struct tab_table *t = r->table;
1218 int rows[3][2], cols[3][2];
1219 int n_row_ranges, n_col_ranges;
1222 /* Rows to render, counting horizontal rules as rows. */
1224 add_range (rows, &n_row_ranges, 0, t->t * 2 + 1);
1225 add_range (rows, &n_row_ranges, r0 * 2 + 1, r1 * 2);
1226 add_range (rows, &n_row_ranges, (t->nr - t->b) * 2, t->nr * 2 + 1);
1228 /* Columns to render, counting vertical rules as columns. */
1230 add_range (cols, &n_col_ranges, 0, r->l * 2 + 1);
1231 add_range (cols, &n_col_ranges, c0 * 2 + 1, c1 * 2);
1232 add_range (cols, &n_col_ranges, (t->nc - r->r) * 2, t->nc * 2 + 1);
1234 y = r->driver->cp_y;
1235 if (!(t->flags & SOMF_NO_TITLE))
1236 y += r->driver->font_height;
1237 for (i = 0; i < n_row_ranges; i++)
1241 for (row = rows[i][0]; row < rows[i][1]; row++)
1245 x = r->driver->cp_x;
1246 for (j = 0; j < n_col_ranges; j++)
1247 x = render_strip (r, x, y, row,
1248 cols[j][0], cols[j][1],
1249 rows[i][0], rows[i][1]);
1251 y += (row & 1) ? r->h[row / 2] : r->hrh[row / 2];
1256 const struct som_table_class tab_table_class =
1272 static enum outp_justification
1273 translate_justification (unsigned int opt)
1275 switch (opt & TAB_ALIGN_MASK)
1288 /* Returns the line style to use for drawing a rule of the given
1290 static enum outp_line_style
1291 rule_to_draw_type (unsigned char type)
1299 return OUTP_L_SINGLE;
1301 return OUTP_L_DOUBLE;
1307 /* Returns the horizontal rule at the given column and row. */
1309 get_hrule (const struct tab_table *t, int col, int row)
1311 return t->rh[col + row * t->cf];
1314 /* Returns the vertical rule at the given column and row. */
1316 get_vrule (const struct tab_table *t, int col, int row)
1318 return t->rv[col + row * (t->cf + 1)];
1321 /* Renders the horizontal rule at the given column and row
1322 at (X,Y) on the page. */
1324 render_horz_rule (const struct tab_rendering *r,
1325 int x, int y, int col, int row)
1327 enum outp_line_style style;
1328 style = rule_to_draw_type (get_hrule (r->table, col, row));
1329 if (style != OUTP_L_NONE)
1330 r->driver->class->line (r->driver, x, y, x + r->w[col], y + r->hrh[row],
1331 OUTP_L_NONE, style, OUTP_L_NONE, style);
1334 /* Renders the vertical rule at the given column and row
1335 at (X,Y) on the page. */
1337 render_vert_rule (const struct tab_rendering *r,
1338 int x, int y, int col, int row)
1340 enum outp_line_style style;
1341 style = rule_to_draw_type (get_vrule (r->table, col, row));
1342 if (style != OUTP_L_NONE)
1343 r->driver->class->line (r->driver, x, y, x + r->wrv[col], y + r->h[row],
1344 style, OUTP_L_NONE, style, OUTP_L_NONE);
1347 /* Renders the rule intersection at the given column and row
1348 at (X,Y) on the page. */
1350 render_rule_intersection (const struct tab_rendering *r,
1351 int x, int y, int col, int row)
1353 const struct tab_table *t = r->table;
1355 /* Bounds of intersection. */
1358 int x1 = x + r->wrv[col];
1359 int y1 = y + r->hrh[row];
1361 /* Lines on each side of intersection. */
1362 int top = row > 0 ? get_vrule (t, col, row - 1) : TAL_0;
1363 int left = col > 0 ? get_hrule (t, col - 1, row) : TAL_0;
1364 int bottom = row < t->nr ? get_vrule (t, col, row) : TAL_0;
1365 int right = col < t->nc ? get_hrule (t, col, row) : TAL_0;
1367 /* Output style for each line. */
1368 enum outp_line_style o_top = rule_to_draw_type (top);
1369 enum outp_line_style o_left = rule_to_draw_type (left);
1370 enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1371 enum outp_line_style o_right = rule_to_draw_type (right);
1373 if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1374 || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1375 r->driver->class->line (r->driver, x0, y0, x1, y1,
1376 o_top, o_left, o_bottom, o_right);
1379 /* Returns the width of columns C1...C2 exclusive,
1380 including interior but not exterior rules. */
1382 strip_width (const struct tab_rendering *r, int c1, int c2)
1387 for (c = c1; c < c2; c++)
1388 width += r->w[c] + r->wrv[c + 1];
1390 width -= r->wrv[c2];
1394 /* Returns the height of rows R1...R2 exclusive,
1395 including interior but not exterior rules. */
1397 strip_height (const struct tab_rendering *r, int r1, int r2)
1402 for (row = r1; row < r2; row++)
1403 height += r->h[row] + r->hrh[row + 1];
1405 height -= r->hrh[r2];
1409 /* Renders the cell at the given column and row at (X,Y) on the
1410 page. Also renders joined cells that extend as far to the
1411 right as C1 and as far down as R1. */
1413 render_cell (const struct tab_rendering *r,
1414 int x, int y, int col, int row, int c1, int r1)
1416 const struct tab_table *t = r->table;
1417 const int index = col + (row * t->cf);
1418 unsigned char type = t->ct[index];
1419 struct substring *content = &t->cc[index];
1421 if (!(type & TAB_JOIN))
1423 if (!(type & TAB_EMPTY))
1425 struct outp_text text;
1426 text.font = options_to_font (type);
1427 text.justification = translate_justification (type);
1428 text.string = *content;
1433 r->driver->class->text_draw (r->driver, &text);
1438 struct tab_joined_cell *j
1439 = (struct tab_joined_cell *) ss_data (*content);
1441 if (j->x1 == col && j->y1 == row)
1443 struct outp_text text;
1444 text.font = options_to_font (type);
1445 text.justification = translate_justification (type);
1446 text.string = j->contents;
1449 text.h = strip_width (r, j->x1, MIN (j->x2, c1));
1450 text.v = strip_height (r, j->y1, MIN (j->y2, r1));
1451 r->driver->class->text_draw (r->driver, &text);
1456 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1457 on row ROW, at (X,Y). Returns X position after rendering.
1458 Also renders joined cells that extend beyond that strip,
1459 cropping them to lie within rendering region (C0,R0)-(C1,R1).
1460 C0 and C1 count vertical rules as columns.
1461 ROW counts horizontal rules as rows, but R0 and R1 do not. */
1463 render_strip (const struct tab_rendering *r,
1464 int x, int y, int row, int c0, int c1, int r0 UNUSED, int r1)
1468 for (col = c0; col < c1; col++)
1472 render_cell (r, x, y, col / 2, row / 2, c1 / 2, r1);
1474 render_horz_rule (r, x, y, col / 2, row / 2);
1480 render_vert_rule (r, x, y, col / 2, row / 2);
1482 render_rule_intersection (r, x, y, col / 2, row / 2);
1483 x += r->wrv[col / 2];