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;
70 t->l = t->r = t->t = t->b = 0;
72 t->cc = pool_nmalloc (t->container, nr * nc, sizeof *t->cc);
73 t->ct = pool_malloc (t->container, nr * nc);
74 memset (t->ct, TAB_EMPTY, nc * nr);
76 t->rh = pool_nmalloc (t->container, nc, nr + 1);
77 memset (t->rh, 0, nc * (nr + 1));
79 t->rv = pool_nmalloc (t->container, nr, nc + 1);
80 memset (t->rv, UCHAR_MAX, nr * (nc + 1));
83 t->col_ofs = t->row_ofs = 0;
88 /* Increases T's reference count and, if this causes T's
89 reference count to reach 0, destroys T. */
91 tab_destroy (struct tab_table *t)
93 assert (t->ref_cnt > 0);
96 if (t->dim_free != NULL)
97 t->dim_free (t->dim_aux);
99 pool_destroy (t->container);
102 /* Increases T's reference count. */
104 tab_ref (struct tab_table *t)
106 assert (t->ref_cnt > 0);
110 /* Sets the width and height of a table, in columns and rows,
111 respectively. Use only to reduce the size of a table, since it
112 does not change the amount of allocated memory. */
114 tab_resize (struct tab_table *t, int nc, int nr)
119 assert (nc + t->col_ofs <= t->cf);
120 t->nc = nc + t->col_ofs;
124 assert (nr + t->row_ofs <= tab_nr (t));
125 t->nr = nr + t->row_ofs;
129 /* Changes either or both dimensions of a table. Consider using the
130 above routine instead if it won't waste a lot of space.
132 Changing the number of columns in a table is particularly expensive
133 in space and time. Avoid doing such. FIXME: In fact, transferring
134 of rules isn't even implemented yet. */
136 tab_realloc (struct tab_table *t, int nc, int nr)
144 tab_offset (t, 0, 0);
151 assert (nc == tab_nc (t));
155 int mr1 = MIN (nr, tab_nr (t));
156 int mc1 = MIN (nc, tab_nc (t));
158 struct substring *new_cc;
159 unsigned char *new_ct;
162 new_cc = pool_nmalloc (t->container, nr * nc, sizeof *new_cc);
163 new_ct = pool_malloc (t->container, nr * nc);
164 for (r = 0; r < mr1; r++)
166 memcpy (&new_cc[r * nc], &t->cc[r * tab_nc (t)], mc1 * sizeof *t->cc);
167 memcpy (&new_ct[r * nc], &t->ct[r * tab_nc (t)], mc1);
168 memset (&new_ct[r * nc + tab_nc (t)], TAB_EMPTY, nc - tab_nc (t));
170 pool_free (t->container, t->cc);
171 pool_free (t->container, t->ct);
176 else if (nr != tab_nr (t))
178 t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc);
179 t->ct = pool_realloc (t->container, t->ct, nr * nc);
181 t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
182 t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
186 memset (&t->rh[nc * (tab_nr (t) + 1)], TAL_0, (nr - tab_nr (t)) * nc);
187 memset (&t->rv[(nc + 1) * tab_nr (t)], UCHAR_MAX,
188 (nr - tab_nr (t)) * (nc + 1));
192 memset (&t->ct[nc * tab_nr (t)], TAB_EMPTY, nc * (nr - tab_nr (t)));
198 tab_offset (t, co, ro);
201 /* Sets the number of header rows on each side of TABLE to L on the
202 left, R on the right, T on the top, B on the bottom. Header rows
203 are repeated when a table is broken across multiple columns or
206 tab_headers (struct tab_table *table, int l, int r, int t, int b)
208 assert (table != NULL);
209 assert (l < table->nc);
210 assert (r < table->nc);
211 assert (t < table->nr);
212 assert (b < table->nr);
221 /* Set up table T so that, when it is an appropriate size, it will be
222 displayed across the page in columns.
224 STYLE is a TAB_COL_* constant. */
226 tab_columns (struct tab_table *t, int style)
229 t->col_style = style;
234 /* Draws a vertical line to the left of cells at horizontal position X
235 from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
237 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
242 if (x + t->col_ofs < 0 || x + t->col_ofs > tab_nc (t)
243 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= tab_nr (t)
244 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= tab_nr (t))
246 printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
247 "table size (%d,%d)\n"),
248 x, t->col_ofs, x + t->col_ofs,
249 y1, t->row_ofs, y1 + t->row_ofs,
250 y2, t->row_ofs, y2 + t->row_ofs,
251 tab_nc (t), tab_nr (t));
261 assert (x < tab_nc (t));
264 assert (y2 <= tab_nr (t));
269 for (y = y1; y <= y2; y++)
270 t->rv[x + (t->cf + 1) * y] = style;
274 /* Draws a horizontal line above cells at vertical position Y from X1
275 to X2 inclusive in style STYLE, if style is not -1. */
277 tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
286 assert (y <= tab_nr (t));
289 assert (x2 < tab_nc (t));
294 for (x = x1; x <= x2; x++)
295 t->rh[x + t->cf * y] = style;
299 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
300 lines of style F_H and vertical lines of style F_V. Fills the
301 interior of the box with horizontal lines of style I_H and vertical
302 lines of style I_V. Any of the line styles may be -1 to avoid
303 drawing those lines. This is distinct from 0, which draws a null
306 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
307 int x1, int y1, int x2, int y2)
312 if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= tab_nc (t)
313 || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= tab_nc (t)
314 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= tab_nr (t)
315 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= tab_nr (t))
317 printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
318 "in table size (%d,%d)\n"),
319 x1, t->col_ofs, x1 + t->col_ofs,
320 y1, t->row_ofs, y1 + t->row_ofs,
321 x2, t->col_ofs, x2 + t->col_ofs,
322 y2, t->row_ofs, y2 + t->row_ofs,
323 tab_nc (t), tab_nr (t));
337 assert (x2 < tab_nc (t));
338 assert (y2 < tab_nr (t));
343 for (x = x1; x <= x2; x++)
345 t->rh[x + t->cf * y1] = f_h;
346 t->rh[x + t->cf * (y2 + 1)] = f_h;
352 for (y = y1; y <= y2; y++)
354 t->rv[x1 + (t->cf + 1) * y] = f_v;
355 t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
363 for (y = y1 + 1; y <= y2; y++)
367 for (x = x1; x <= x2; x++)
368 t->rh[x + t->cf * y] = i_h;
375 for (x = x1 + 1; x <= x2; x++)
379 for (y = y1; y <= y2; y++)
380 t->rv[x + (t->cf + 1) * y] = i_v;
385 /* Formats text TEXT and arguments ARGS as indicated in OPT in
386 TABLE's pool and returns the resultant string. */
387 static struct substring
388 text_format (struct tab_table *table, int opt, const char *text, va_list args)
390 assert (table != NULL && text != NULL);
392 return ss_cstr (opt & TAT_PRINTF
393 ? pool_vasprintf (table->container, text, args)
394 : pool_strdup (table->container, text));
397 /* Set the title of table T to TITLE, which is formatted as if
398 passed to printf(). */
400 tab_title (struct tab_table *t, const char *title, ...)
404 assert (t != NULL && title != NULL);
405 va_start (args, title);
406 t->title = xvasprintf (title, args);
410 /* Set DIM_FUNC, which will be passed auxiliary data AUX, as the
411 dimension function for table T.
413 DIM_FUNC must not assume that it is called from the same
414 context as tab_dim; for example, table T might be kept in
415 memory and, thus, DIM_FUNC might be called after the currently
416 running command completes. If it is non-null, FREE_FUNC is
417 called when the table is destroyed, to allow any data
418 allocated for use by DIM_FUNC to be freed. */
420 tab_dim (struct tab_table *t,
421 tab_dim_func *dim_func, tab_dim_free_func *free_func, void *aux)
423 assert (t->dim == NULL);
425 t->dim_free = free_func;
429 /* Returns the natural width of column C in table T for driver D, that
430 is, the smallest width necessary to display all its cells without
431 wrapping. The width will be no larger than the page width minus
432 left and right rule widths. */
434 tab_natural_width (const struct tab_rendering *r, int col)
436 const struct tab_table *t = r->table;
437 int width, row, max_width;
439 assert (col >= 0 && col < tab_nc (t));
442 for (row = 0; row < tab_nr (t); row++)
444 struct outp_text text;
445 unsigned char opt = t->ct[col + row * t->cf];
448 if (opt & (TAB_JOIN | TAB_EMPTY))
451 text.string = t->cc[col + row * t->cf];
452 text.justification = OUTP_LEFT;
453 text.font = options_to_font (opt);
454 text.h = text.v = INT_MAX;
456 r->driver->class->text_metrics (r->driver, &text, &w, NULL);
463 /* FIXME: This is an ugly kluge to compensate for the fact
464 that we don't let joined cells contribute to column
466 width = r->driver->prop_em_width * 8;
469 max_width = r->driver->width - r->wrv[0] - r->wrv[tab_nc (t)];
470 return MIN (width, max_width);
473 /* Returns the natural height of row R in table T for driver D, that
474 is, the minimum height necessary to display the information in the
475 cell at the widths set for each column. */
477 tab_natural_height (const struct tab_rendering *r, int row)
479 const struct tab_table *t = r->table;
482 assert (row >= 0 && row < tab_nr (t));
484 height = r->driver->font_height;
485 for (col = 0; col < tab_nc (t); col++)
487 struct outp_text text;
488 unsigned char opt = t->ct[col + row * t->cf];
491 if (opt & (TAB_JOIN | TAB_EMPTY))
494 text.string = t->cc[col + row * t->cf];
495 text.justification = OUTP_LEFT;
496 text.font = options_to_font (opt);
499 r->driver->class->text_metrics (r->driver, &text, NULL, &h);
508 /* Callback function to set all columns and rows to their natural
509 dimensions. Not really meant to be called directly. */
511 tab_natural_dimensions (struct tab_rendering *r, void *aux UNUSED)
513 const struct tab_table *t = r->table;
516 for (i = 0; i < tab_nc (t); i++)
517 r->w[i] = tab_natural_width (r, i);
519 for (i = 0; i < tab_nr (t); i++)
520 r->h[i] = tab_natural_height (r, i);
526 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
527 from V, displayed with format spec F. */
529 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
530 const union value *v, const struct fmt_spec *f)
534 assert (table != NULL && v != NULL && f != NULL);
536 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
537 || c + table->col_ofs >= tab_nc (table)
538 || r + table->row_ofs >= tab_nr (table))
540 printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
542 c, table->col_ofs, c + table->col_ofs,
543 r, table->row_ofs, r + table->row_ofs,
544 tab_nc (table), tab_nr (table));
549 contents = pool_alloc (table->container, f->w);
550 table->cc[c + r * table->cf] = ss_buffer (contents, f->w);
551 table->ct[c + r * table->cf] = opt;
553 data_out (v, f, contents);
556 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
557 with NDEC decimal places. */
559 tab_fixed (struct tab_table *table, int c, int r, unsigned char opt,
560 double val, int w, int d)
566 union value double_value;
568 assert (table != NULL && w <= 40);
571 assert (c < tab_nc (table));
573 assert (r < tab_nr (table));
575 f = fmt_for_output (FMT_F, w, d);
578 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
579 || c + table->col_ofs >= tab_nc (table)
580 || r + table->row_ofs >= tab_nr (table))
582 printf ("tab_fixed(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
584 c, table->col_ofs, c + table->col_ofs,
585 r, table->row_ofs, r + table->row_ofs,
586 tab_nc (table), tab_nr (table));
591 double_value.f = val;
592 data_out (&double_value, &f, buf);
595 while (isspace ((unsigned char) *cp) && cp < &buf[w])
597 f.w = w - (cp - buf);
599 contents = pool_alloc (table->container, f.w);
600 table->cc[c + r * table->cf] = ss_buffer (contents, f.w);
601 table->ct[c + r * table->cf] = opt;
602 memcpy (contents, cp, f.w);
605 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL as
607 If FMT is null, then the default print format will be used.
610 tab_double (struct tab_table *table, int c, int r, unsigned char opt,
611 double val, const struct fmt_spec *fmt)
617 union value double_value;
619 assert (table != NULL);
622 assert (c < tab_nc (table));
624 assert (r < tab_nr (table));
627 fmt = settings_get_format ();
629 fmt_check_output (fmt);
632 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
633 || c + table->col_ofs >= tab_nc (table)
634 || r + table->row_ofs >= tab_nr (table))
636 printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
638 c, table->col_ofs, c + table->col_ofs,
639 r, table->row_ofs, r + table->row_ofs,
640 tab_nc (table), tab_nr (table));
645 double_value.f = val;
646 data_out (&double_value, fmt, buf);
649 while (isspace ((unsigned char) *cp) && cp < &buf[fmt->w])
651 w = fmt->w - (cp - buf);
653 contents = pool_alloc (table->container, w);
654 table->cc[c + r * table->cf] = ss_buffer (contents, w);
655 table->ct[c + r * table->cf] = opt;
656 memcpy (contents, cp, w);
660 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
663 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
667 assert (table != NULL && text != NULL);
671 assert (c < tab_nc (table));
672 assert (r < tab_nr (table));
676 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
677 || c + table->col_ofs >= tab_nc (table)
678 || r + table->row_ofs >= tab_nr (table))
680 printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
682 c, table->col_ofs, c + table->col_ofs,
683 r, table->row_ofs, r + table->row_ofs,
684 tab_nc (table), tab_nr (table));
689 va_start (args, text);
690 table->cc[c + r * table->cf] = text_format (table, opt, text, args);
691 table->ct[c + r * table->cf] = opt;
695 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
696 options OPT to have text value TEXT. */
698 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
699 unsigned opt, const char *text, ...)
701 struct tab_joined_cell *j;
703 assert (table != NULL && text != NULL);
705 assert (x1 + table->col_ofs >= 0);
706 assert (y1 + table->row_ofs >= 0);
709 assert (y2 + table->row_ofs < tab_nr (table));
710 assert (x2 + table->col_ofs < tab_nc (table));
713 if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= tab_nc (table)
714 || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= tab_nr (table)
715 || x2 < x1 || x2 + table->col_ofs >= tab_nc (table)
716 || y2 < y2 || y2 + table->row_ofs >= tab_nr (table))
718 printf ("tab_joint_text(): bad cell "
719 "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
720 x1, table->col_ofs, x1 + table->col_ofs,
721 y1, table->row_ofs, y1 + table->row_ofs,
722 x2, table->col_ofs, x2 + table->col_ofs,
723 y2, table->row_ofs, y2 + table->row_ofs,
724 tab_nc (table), tab_nr (table));
729 tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
731 j = pool_alloc (table->container, sizeof *j);
732 j->x1 = x1 + table->col_ofs;
733 j->y1 = y1 + table->row_ofs;
734 j->x2 = ++x2 + table->col_ofs;
735 j->y2 = ++y2 + table->row_ofs;
740 va_start (args, text);
741 j->contents = text_format (table, opt, text, args);
748 struct substring *cc = &table->cc[x1 + y1 * table->cf];
749 unsigned char *ct = &table->ct[x1 + y1 * table->cf];
750 const int ofs = table->cf - (x2 - x1);
754 for (y = y1; y < y2; y++)
758 for (x = x1; x < x2; x++)
760 *cc++ = ss_buffer ((char *) j, 0);
770 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
772 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
773 struct substring *string)
775 assert (table != NULL && string != NULL);
778 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
779 || c + table->col_ofs >= tab_nc (table)
780 || r + table->row_ofs >= tab_nr (table))
782 printf ("tab_raw(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
784 c, table->col_ofs, c + table->col_ofs,
785 r, table->row_ofs, r + table->row_ofs,
786 tab_nc (table), tab_nr (table));
791 table->cc[c + r * table->cf] = *string;
792 table->ct[c + r * table->cf] = opt;
797 /* Sets the widths of all the columns and heights of all the rows in
798 table T for driver D. */
800 nowrap_dim (struct tab_rendering *r, void *aux UNUSED)
802 r->w[0] = tab_natural_width (r, 0);
803 r->h[0] = r->driver->font_height;
806 /* Sets the widths of all the columns and heights of all the rows in
807 table T for driver D. */
809 wrap_dim (struct tab_rendering *r, void *aux UNUSED)
811 r->w[0] = tab_natural_width (r, 0);
812 r->h[0] = tab_natural_height (r, 0);
815 /* Outputs text BUF as a table with a single cell having cell options
816 OPTIONS, which is a combination of the TAB_* and TAT_*
819 tab_output_text (int options, const char *buf, ...)
821 struct tab_table *t = tab_create (1, 1, 0);
822 char *tmp_buf = NULL;
824 if (options & TAT_PRINTF)
828 va_start (args, buf);
829 buf = tmp_buf = xvasprintf (buf, args);
833 tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
834 tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
835 tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL, NULL);
841 /* Set table flags to FLAGS. */
843 tab_flags (struct tab_table *t, unsigned flags)
849 /* Easy, type-safe way to submit a tab table to som. */
851 tab_submit (struct tab_table *t)
856 s.class = &tab_table_class;
865 /* Set table row and column offsets for all functions that affect
868 tab_offset (struct tab_table *t, int col, int row)
874 if (row < -1 || row > tab_nr (t))
876 printf ("tab_offset(): row=%d in %d-row table\n", row, tab_nr (t));
879 if (col < -1 || col > tab_nc (t))
881 printf ("tab_offset(): col=%d in %d-column table\n", col, tab_nc (t));
887 diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
889 diff += (col - t->col_ofs), t->col_ofs = col;
895 /* Increment the row offset by one. If the table is too small,
896 increase its size. */
898 tab_next_row (struct tab_table *t)
903 if (++t->row_ofs >= tab_nr (t))
904 tab_realloc (t, -1, tab_nr (t) * 4 / 3);
907 /* Return the number of columns and rows in the table into N_COLUMNS
908 and N_ROWS, respectively. */
910 tabi_count (struct som_entity *t_, int *n_columns, int *n_rows)
912 struct tab_table *t = t_->ext;
917 /* Return the column style for this table into STYLE. */
919 tabi_columns (struct som_entity *t_, int *style)
921 struct tab_table *t = t_->ext;
922 *style = t->col_style;
925 /* Return the number of header rows/columns on the left, right, top,
926 and bottom sides into HL, HR, HT, and HB, respectively. */
928 tabi_headers (struct som_entity *t_, int *hl, int *hr, int *ht, int *hb)
930 struct tab_table *t = t_->ext;
937 /* Return flags set for the current table into FLAGS. */
939 tabi_flags (struct som_entity *t_, unsigned *flags)
941 struct tab_table *t = t_->ext;
945 /* Returns the line style to use for spacing purposes for a rule
946 of the given TYPE. */
947 static enum outp_line_style
948 rule_to_spacing_type (unsigned char type)
956 return OUTP_L_SINGLE;
958 return OUTP_L_DOUBLE;
965 tabi_render_init (struct som_entity *t_, struct outp_driver *driver,
966 int hl, int hr, int ht, int hb)
968 const struct tab_table *t = t_->ext;
969 struct tab_rendering *r;
973 tab_offset (t_->ext, 0, 0);
975 r = xmalloc (sizeof *r);
978 r->w = xnmalloc (tab_nc (t), sizeof *r->w);
979 r->h = xnmalloc (tab_nr (t), sizeof *r->h);
980 r->hrh = xnmalloc (tab_nr (t) + 1, sizeof *r->hrh);
981 r->wrv = xnmalloc (tab_nc (t) + 1, sizeof *r->wrv);
987 /* Figure out sizes of rules. */
988 for (row = 0; row <= tab_nr (t); row++)
991 for (col = 0; col < tab_nc (t); col++)
993 unsigned char rh = t->rh[col + row * t->cf];
994 int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
1001 for (col = 0; col <= tab_nc (t); col++)
1004 for (row = 0; row < tab_nr (t); row++)
1006 unsigned char *rv = &t->rv[col + row * (t->cf + 1)];
1008 if (*rv == UCHAR_MAX)
1009 *rv = col != 0 && col != tab_nc (t) ? TAL_GAP : TAL_0;
1010 w = driver->vert_line_width[rule_to_spacing_type (*rv)];
1014 r->wrv[col] = width;
1017 /* Determine row heights and columns widths. */
1018 for (i = 0; i < tab_nr (t); i++)
1020 for (i = 0; i < tab_nc (t); i++)
1023 t->dim (r, t->dim_aux);
1025 for (i = 0; i < tab_nr (t); i++)
1027 error (0, 0, "height of table row %d is %d (not initialized?)",
1029 for (i = 0; i < tab_nc (t); i++)
1031 error (0, 0, "width of table column %d is %d (not initialized?)",
1034 /* Add up header sizes. */
1035 for (i = 0, r->wl = r->wrv[0]; i < r->l; i++)
1036 r->wl += r->w[i] + r->wrv[i + 1];
1037 for (i = 0, r->ht = r->hrh[0]; i < r->t; i++)
1038 r->ht += r->h[i] + r->hrh[i + 1];
1039 for (i = tab_nc (t) - r->r, r->wr = r->wrv[i]; i < tab_nc (t); i++)
1040 r->wr += r->w[i] + r->wrv[i + 1];
1041 for (i = tab_nr (t) - r->b, r->hb = r->hrh[i]; i < tab_nr (t); i++)
1042 r->hb += r->h[i] + r->hrh[i + 1];
1045 if (!(t->flags & SOMF_NO_TITLE))
1046 r->ht += driver->font_height;
1052 tabi_render_free (void *r_)
1054 struct tab_rendering *r = r_;
1063 /* Return the horizontal and vertical size of the entire table,
1064 including headers, for the current output device, into HORIZ and
1067 tabi_area (void *r_, int *horiz, int *vert)
1069 struct tab_rendering *r = r_;
1070 const struct tab_table *t = r->table;
1075 for (col = r->l + 1, width = r->wl + r->wr + r->w[tab_l (t)];
1076 col < tab_nc (t) - r->r; col++)
1077 width += r->w[col] + r->wrv[col];
1081 for (row = r->t + 1, height = r->ht + r->hb + r->h[tab_t (t)];
1082 row < tab_nr (t) - tab_b (t); row++)
1083 height += r->h[row] + r->hrh[row];
1087 /* Determines the number of rows or columns (including appropriate
1088 headers), depending on CUMTYPE, that will fit into the space
1089 specified. Takes rows/columns starting at index START and attempts
1090 to fill up available space MAX. Returns in END the index of the
1091 last row/column plus one; returns in ACTUAL the actual amount of
1092 space the selected rows/columns (including appropriate headers)
1095 tabi_cumulate (void *r_, int cumtype, int start, int *end,
1096 int max, int *actual)
1098 const struct tab_rendering *r = r_;
1099 const struct tab_table *t = r->table;
1105 assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1106 if (cumtype == SOM_ROWS)
1108 assert (start >= 0 && start < tab_nr (t));
1109 limit = tab_nr (t) - r->b;
1110 cells = &r->h[start];
1111 rules = &r->hrh[start + 1];
1112 total = r->ht + r->hb;
1116 assert (start >= 0 && start < tab_nc (t));
1117 limit = tab_nc (t) - tab_r (t);
1118 cells = &r->w[start];
1119 rules = &r->wrv[start + 1];
1120 total = r->wl + r->wr;
1133 for (idx = start + 1; idx < limit; idx++)
1135 int amt = *cells++ + *rules++;
1152 /* Render title for current table, with major index X and minor index
1153 Y. Y may be zero, or X and Y may be zero, but X should be nonzero
1156 tabi_title (void *r_, int x, int y, int table_num, int subtable_num,
1157 const char *command_name)
1159 const struct tab_rendering *r = r_;
1160 const struct tab_table *t = r->table;
1161 struct outp_text text;
1162 struct string title;
1164 if (t->flags & SOMF_NO_TITLE)
1167 ds_init_empty (&title);
1168 ds_put_format (&title,"%d.%d", table_num, subtable_num);
1170 ds_put_format (&title, "(%d:%d)", x, y);
1172 ds_put_format (&title, "(%d)", x);
1173 if (command_name != NULL)
1174 ds_put_format (&title, " %s", command_name);
1175 ds_put_cstr (&title, ". ");
1176 if (t->title != NULL)
1177 ds_put_cstr (&title, t->title);
1179 text.font = OUTP_PROPORTIONAL;
1180 text.justification = OUTP_LEFT;
1181 text.string = ds_ss (&title);
1182 text.h = r->driver->width;
1183 text.v = r->driver->font_height;
1185 text.y = r->driver->cp_y;
1186 r->driver->class->text_draw (r->driver, &text);
1188 ds_destroy (&title);
1191 static int render_strip (const struct tab_rendering *,
1192 int x, int y, int r, int c1, int c2, int r1, int r2);
1195 add_range (int ranges[][2], int *np, int start, int end)
1198 if (n == 0 || start > ranges[n - 1][1])
1200 ranges[n][0] = start;
1205 ranges[n - 1][1] = end;
1208 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1209 current position on the current output device. */
1211 tabi_render (void *r_, int c0, int r0, int c1, int r1)
1213 const struct tab_rendering *r = r_;
1214 const struct tab_table *t = r->table;
1215 int rows[3][2], cols[3][2];
1216 int n_row_ranges, n_col_ranges;
1219 /* Rows to render, counting horizontal rules as rows. */
1221 add_range (rows, &n_row_ranges, 0, tab_t (t) * 2 + 1);
1222 add_range (rows, &n_row_ranges, r0 * 2 + 1, r1 * 2);
1223 add_range (rows, &n_row_ranges, (tab_nr (t) - tab_b (t)) * 2,
1224 tab_nr (t) * 2 + 1);
1226 /* Columns to render, counting vertical rules as columns. */
1228 add_range (cols, &n_col_ranges, 0, r->l * 2 + 1);
1229 add_range (cols, &n_col_ranges, c0 * 2 + 1, c1 * 2);
1230 add_range (cols, &n_col_ranges, (tab_nc (t) - r->r) * 2, tab_nc (t) * 2 + 1);
1232 y = r->driver->cp_y;
1233 if (!(t->flags & SOMF_NO_TITLE))
1234 y += r->driver->font_height;
1235 for (i = 0; i < n_row_ranges; i++)
1239 for (row = rows[i][0]; row < rows[i][1]; row++)
1243 x = r->driver->cp_x;
1244 for (j = 0; j < n_col_ranges; j++)
1245 x = render_strip (r, x, y, row,
1246 cols[j][0], cols[j][1],
1247 rows[i][0], rows[i][1]);
1249 y += (row & 1) ? r->h[row / 2] : r->hrh[row / 2];
1254 const struct som_table_class tab_table_class =
1270 static enum outp_justification
1271 translate_justification (unsigned int opt)
1273 switch (opt & TAB_ALIGN_MASK)
1286 /* Returns the line style to use for drawing a rule of the given
1288 static enum outp_line_style
1289 rule_to_draw_type (unsigned char type)
1297 return OUTP_L_SINGLE;
1299 return OUTP_L_DOUBLE;
1305 /* Returns the horizontal rule at the given column and row. */
1307 get_hrule (const struct tab_table *t, int col, int row)
1309 return t->rh[col + row * t->cf];
1312 /* Returns the vertical rule at the given column and row. */
1314 get_vrule (const struct tab_table *t, int col, int row)
1316 return t->rv[col + row * (t->cf + 1)];
1319 /* Renders the horizontal rule at the given column and row
1320 at (X,Y) on the page. */
1322 render_horz_rule (const struct tab_rendering *r,
1323 int x, int y, int col, int row)
1325 enum outp_line_style style;
1326 style = rule_to_draw_type (get_hrule (r->table, col, row));
1327 if (style != OUTP_L_NONE)
1328 r->driver->class->line (r->driver, x, y, x + r->w[col], y + r->hrh[row],
1329 OUTP_L_NONE, style, OUTP_L_NONE, style);
1332 /* Renders the vertical rule at the given column and row
1333 at (X,Y) on the page. */
1335 render_vert_rule (const struct tab_rendering *r,
1336 int x, int y, int col, int row)
1338 enum outp_line_style style;
1339 style = rule_to_draw_type (get_vrule (r->table, col, row));
1340 if (style != OUTP_L_NONE)
1341 r->driver->class->line (r->driver, x, y, x + r->wrv[col], y + r->h[row],
1342 style, OUTP_L_NONE, style, OUTP_L_NONE);
1345 /* Renders the rule intersection at the given column and row
1346 at (X,Y) on the page. */
1348 render_rule_intersection (const struct tab_rendering *r,
1349 int x, int y, int col, int row)
1351 const struct tab_table *t = r->table;
1353 /* Bounds of intersection. */
1356 int x1 = x + r->wrv[col];
1357 int y1 = y + r->hrh[row];
1359 /* Lines on each side of intersection. */
1360 int top = row > 0 ? get_vrule (t, col, row - 1) : TAL_0;
1361 int left = col > 0 ? get_hrule (t, col - 1, row) : TAL_0;
1362 int bottom = row < tab_nr (t) ? get_vrule (t, col, row) : TAL_0;
1363 int right = col < tab_nc (t) ? get_hrule (t, col, row) : TAL_0;
1365 /* Output style for each line. */
1366 enum outp_line_style o_top = rule_to_draw_type (top);
1367 enum outp_line_style o_left = rule_to_draw_type (left);
1368 enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1369 enum outp_line_style o_right = rule_to_draw_type (right);
1371 if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1372 || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1373 r->driver->class->line (r->driver, x0, y0, x1, y1,
1374 o_top, o_left, o_bottom, o_right);
1377 /* Returns the width of columns C1...C2 exclusive,
1378 including interior but not exterior rules. */
1380 strip_width (const struct tab_rendering *r, int c1, int c2)
1385 for (c = c1; c < c2; c++)
1386 width += r->w[c] + r->wrv[c + 1];
1388 width -= r->wrv[c2];
1392 /* Returns the height of rows R1...R2 exclusive,
1393 including interior but not exterior rules. */
1395 strip_height (const struct tab_rendering *r, int r1, int r2)
1400 for (row = r1; row < r2; row++)
1401 height += r->h[row] + r->hrh[row + 1];
1403 height -= r->hrh[r2];
1407 /* Renders the cell at the given column and row at (X,Y) on the
1408 page. Also renders joined cells that extend as far to the
1409 right as C1 and as far down as R1. */
1411 render_cell (const struct tab_rendering *r,
1412 int x, int y, int col, int row, int c1, int r1)
1414 const struct tab_table *t = r->table;
1415 const int index = col + (row * t->cf);
1416 unsigned char type = t->ct[index];
1417 struct substring *content = &t->cc[index];
1419 if (!(type & TAB_JOIN))
1421 if (!(type & TAB_EMPTY))
1423 struct outp_text text;
1424 text.font = options_to_font (type);
1425 text.justification = translate_justification (type);
1426 text.string = *content;
1431 r->driver->class->text_draw (r->driver, &text);
1436 struct tab_joined_cell *j
1437 = (struct tab_joined_cell *) ss_data (*content);
1439 if (j->x1 == col && j->y1 == row)
1441 struct outp_text text;
1442 text.font = options_to_font (type);
1443 text.justification = translate_justification (type);
1444 text.string = j->contents;
1447 text.h = strip_width (r, j->x1, MIN (j->x2, c1));
1448 text.v = strip_height (r, j->y1, MIN (j->y2, r1));
1449 r->driver->class->text_draw (r->driver, &text);
1454 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1455 on row ROW, at (X,Y). Returns X position after rendering.
1456 Also renders joined cells that extend beyond that strip,
1457 cropping them to lie within rendering region (C0,R0)-(C1,R1).
1458 C0 and C1 count vertical rules as columns.
1459 ROW counts horizontal rules as rows, but R0 and R1 do not. */
1461 render_strip (const struct tab_rendering *r,
1462 int x, int y, int row, int c0, int c1, int r0 UNUSED, int r1)
1466 for (col = c0; col < c1; col++)
1470 render_cell (r, x, y, col / 2, row / 2, c1 / 2, r1);
1472 render_horz_rule (r, x, y, col / 2, row / 2);
1478 render_vert_rule (r, x, y, col / 2, row / 2);
1480 render_rule_intersection (r, x, y, col / 2, row / 2);
1481 x += r->wrv[col / 2];