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 <data/dictionary.h>
33 #include <libpspp/assertion.h>
34 #include <libpspp/compiler.h>
35 #include <libpspp/misc.h>
36 #include <libpspp/pool.h>
38 #include <data/settings.h>
45 #define _(msgid) gettext (msgid)
47 const struct som_table_class tab_table_class;
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)
64 t = pool_create_container (struct tab_table, container);
66 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 <= tab_nr (t));
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 == tab_nc (t));
156 int mr1 = MIN (nr, tab_nr (t));
157 int mc1 = MIN (nc, tab_nc (t));
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 * tab_nc (t)], mc1 * sizeof *t->cc);
168 memcpy (&new_ct[r * nc], &t->ct[r * tab_nc (t)], mc1);
169 memset (&new_ct[r * nc + tab_nc (t)], TAB_EMPTY, nc - tab_nc (t));
171 pool_free (t->container, t->cc);
172 pool_free (t->container, t->ct);
177 else if (nr != tab_nr (t))
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 * (tab_nr (t) + 1)], TAL_0, (nr - tab_nr (t)) * nc);
188 memset (&t->rv[(nc + 1) * tab_nr (t)], UCHAR_MAX,
189 (nr - tab_nr (t)) * (nc + 1));
193 memset (&t->ct[nc * tab_nr (t)], TAB_EMPTY, nc * (nr - tab_nr (t)));
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. */
227 tab_columns (struct tab_table *t, int style)
230 t->col_style = style;
235 /* Draws a vertical line to the left of cells at horizontal position X
236 from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
238 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
243 if (x + t->col_ofs < 0 || x + t->col_ofs > tab_nc (t)
244 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= tab_nr (t)
245 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= tab_nr (t))
247 printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
248 "table size (%d,%d)\n"),
249 x, t->col_ofs, x + t->col_ofs,
250 y1, t->row_ofs, y1 + t->row_ofs,
251 y2, t->row_ofs, y2 + t->row_ofs,
252 tab_nc (t), tab_nr (t));
262 assert (x < tab_nc (t));
265 assert (y2 <= tab_nr (t));
270 for (y = y1; y <= y2; y++)
271 t->rv[x + (t->cf + 1) * y] = style;
275 /* Draws a horizontal line above cells at vertical position Y from X1
276 to X2 inclusive in style STYLE, if style is not -1. */
278 tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
287 assert (y <= tab_nr (t));
290 assert (x2 < tab_nc (t));
295 for (x = x1; x <= x2; x++)
296 t->rh[x + t->cf * y] = style;
300 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
301 lines of style F_H and vertical lines of style F_V. Fills the
302 interior of the box with horizontal lines of style I_H and vertical
303 lines of style I_V. Any of the line styles may be -1 to avoid
304 drawing those lines. This is distinct from 0, which draws a null
307 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
308 int x1, int y1, int x2, int y2)
313 if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= tab_nc (t)
314 || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= tab_nc (t)
315 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= tab_nr (t)
316 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= tab_nr (t))
318 printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
319 "in table size (%d,%d)\n"),
320 x1, t->col_ofs, x1 + t->col_ofs,
321 y1, t->row_ofs, y1 + t->row_ofs,
322 x2, t->col_ofs, x2 + t->col_ofs,
323 y2, t->row_ofs, y2 + t->row_ofs,
324 tab_nc (t), tab_nr (t));
338 assert (x2 < tab_nc (t));
339 assert (y2 < tab_nr (t));
344 for (x = x1; x <= x2; x++)
346 t->rh[x + t->cf * y1] = f_h;
347 t->rh[x + t->cf * (y2 + 1)] = f_h;
353 for (y = y1; y <= y2; y++)
355 t->rv[x1 + (t->cf + 1) * y] = f_v;
356 t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
364 for (y = y1 + 1; y <= y2; y++)
368 for (x = x1; x <= x2; x++)
369 t->rh[x + t->cf * y] = i_h;
376 for (x = x1 + 1; x <= x2; x++)
380 for (y = y1; y <= y2; y++)
381 t->rv[x + (t->cf + 1) * y] = i_v;
386 /* Set the title of table T to TITLE, which is formatted as if
387 passed to printf(). */
389 tab_title (struct tab_table *t, const char *title, ...)
393 assert (t != NULL && title != NULL);
394 va_start (args, title);
395 t->title = xvasprintf (title, args);
399 /* Set DIM_FUNC, which will be passed auxiliary data AUX, as the
400 dimension function for table T.
402 DIM_FUNC must not assume that it is called from the same
403 context as tab_dim; for example, table T might be kept in
404 memory and, thus, DIM_FUNC might be called after the currently
405 running command completes. If it is non-null, FREE_FUNC is
406 called when the table is destroyed, to allow any data
407 allocated for use by DIM_FUNC to be freed. */
409 tab_dim (struct tab_table *t,
410 tab_dim_func *dim_func, tab_dim_free_func *free_func, void *aux)
412 assert (t->dim == NULL);
414 t->dim_free = free_func;
418 /* Returns the natural width of column C in table T for driver D, that
419 is, the smallest width necessary to display all its cells without
420 wrapping. The width will be no larger than the page width minus
421 left and right rule widths. */
423 tab_natural_width (const struct tab_rendering *r, int col)
425 const struct tab_table *t = r->table;
426 int width, row, max_width;
428 assert (col >= 0 && col < tab_nc (t));
431 for (row = 0; row < tab_nr (t); row++)
433 struct outp_text text;
434 unsigned char opt = t->ct[col + row * t->cf];
437 if (opt & (TAB_JOIN | TAB_EMPTY))
440 text.string = t->cc[col + row * t->cf];
441 text.justification = OUTP_LEFT;
442 text.font = options_to_font (opt);
443 text.h = text.v = INT_MAX;
445 r->driver->class->text_metrics (r->driver, &text, &w, NULL);
452 /* FIXME: This is an ugly kluge to compensate for the fact
453 that we don't let joined cells contribute to column
455 width = r->driver->prop_em_width * 8;
458 max_width = r->driver->width - r->wrv[0] - r->wrv[tab_nc (t)];
459 return MIN (width, max_width);
462 /* Returns the natural height of row R in table T for driver D, that
463 is, the minimum height necessary to display the information in the
464 cell at the widths set for each column. */
466 tab_natural_height (const struct tab_rendering *r, int row)
468 const struct tab_table *t = r->table;
471 assert (row >= 0 && row < tab_nr (t));
473 height = r->driver->font_height;
474 for (col = 0; col < tab_nc (t); col++)
476 struct outp_text text;
477 unsigned char opt = t->ct[col + row * t->cf];
480 if (opt & (TAB_JOIN | TAB_EMPTY))
483 text.string = t->cc[col + row * t->cf];
484 text.justification = OUTP_LEFT;
485 text.font = options_to_font (opt);
488 r->driver->class->text_metrics (r->driver, &text, NULL, &h);
497 /* Callback function to set all columns and rows to their natural
498 dimensions. Not really meant to be called directly. */
500 tab_natural_dimensions (struct tab_rendering *r, void *aux UNUSED)
502 const struct tab_table *t = r->table;
505 for (i = 0; i < tab_nc (t); i++)
506 r->w[i] = tab_natural_width (r, i);
508 for (i = 0; i < tab_nr (t); i++)
509 r->h[i] = tab_natural_height (r, i);
515 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
516 from V, displayed with format spec F. */
518 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
519 const union value *v, const struct dictionary *dict,
520 const struct fmt_spec *f)
524 assert (table != NULL && v != NULL && f != NULL);
526 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
527 || c + table->col_ofs >= tab_nc (table)
528 || r + table->row_ofs >= tab_nr (table))
530 printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
532 c, table->col_ofs, c + table->col_ofs,
533 r, table->row_ofs, r + table->row_ofs,
534 tab_nc (table), tab_nr (table));
539 contents = data_out_pool (v, dict_get_encoding (dict), f, table->container);
541 table->cc[c + r * table->cf] = ss_cstr (contents);
542 table->ct[c + r * table->cf] = opt;
545 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
546 with NDEC decimal places. */
548 tab_fixed (struct tab_table *table, int c, int r, unsigned char opt,
549 double val, int w, int d)
554 union value double_value;
556 assert (table != NULL && w <= 40);
559 assert (c < tab_nc (table));
561 assert (r < tab_nr (table));
563 f = fmt_for_output (FMT_F, w, d);
566 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
567 || c + table->col_ofs >= tab_nc (table)
568 || r + table->row_ofs >= tab_nr (table))
570 printf ("tab_fixed(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
572 c, table->col_ofs, c + table->col_ofs,
573 r, table->row_ofs, r + table->row_ofs,
574 tab_nc (table), tab_nr (table));
579 double_value.f = val;
580 s = data_out_pool (&double_value, LEGACY_NATIVE, &f, table->container);
583 while (isspace ((unsigned char) *cp) && cp < &s[w])
587 table->cc[c + r * table->cf] = ss_buffer (cp, f.w);
588 table->ct[c + r * table->cf] = opt;
591 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL as
593 If FMT is null, then the default print format will be used.
596 tab_double (struct tab_table *table, int c, int r, unsigned char opt,
597 double val, const struct fmt_spec *fmt)
600 union value double_value ;
602 assert (table != NULL);
605 assert (c < tab_nc (table));
607 assert (r < tab_nr (table));
610 fmt = settings_get_format ();
612 fmt_check_output (fmt);
615 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
616 || c + table->col_ofs >= tab_nc (table)
617 || r + table->row_ofs >= tab_nr (table))
619 printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
621 c, table->col_ofs, c + table->col_ofs,
622 r, table->row_ofs, r + table->row_ofs,
623 tab_nc (table), tab_nr (table));
628 double_value.f = val;
629 ss = ss_cstr (data_out_pool (&double_value, LEGACY_NATIVE, fmt, table->container));
631 ss_ltrim (&ss, ss_cstr (" "));
633 table->cc[c + r * table->cf] = ss;
634 table->ct[c + r * table->cf] = opt;
639 do_tab_text (struct tab_table *table, int c, int r, unsigned opt, char *text)
643 assert (c < tab_nc (table));
644 assert (r < tab_nr (table));
647 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
648 || c + table->col_ofs >= tab_nc (table)
649 || r + table->row_ofs >= tab_nr (table))
651 printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
653 c, table->col_ofs, c + table->col_ofs,
654 r, table->row_ofs, r + table->row_ofs,
655 tab_nc (table), tab_nr (table));
660 table->cc[c + r * table->cf] = ss_cstr (text);
661 table->ct[c + r * table->cf] = opt;
664 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
667 tab_text (struct tab_table *table, int c, int r, unsigned opt,
670 do_tab_text (table, c, r, opt, pool_strdup (table->container, text));
673 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
674 FORMAT, which is formatted as if passed to printf. */
676 tab_text_format (struct tab_table *table, int c, int r, unsigned opt,
677 const char *format, ...)
681 va_start (args, format);
682 do_tab_text (table, c, r, opt,
683 pool_vasprintf (table->container, format, args));
688 do_tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
689 unsigned opt, char *text)
691 struct tab_joined_cell *j;
693 assert (x1 + table->col_ofs >= 0);
694 assert (y1 + table->row_ofs >= 0);
697 assert (y2 + table->row_ofs < tab_nr (table));
698 assert (x2 + table->col_ofs < tab_nc (table));
701 if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= tab_nc (table)
702 || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= tab_nr (table)
703 || x2 < x1 || x2 + table->col_ofs >= tab_nc (table)
704 || y2 < y2 || y2 + table->row_ofs >= tab_nr (table))
706 printf ("tab_joint_text(): bad cell "
707 "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
708 x1, table->col_ofs, x1 + table->col_ofs,
709 y1, table->row_ofs, y1 + table->row_ofs,
710 x2, table->col_ofs, x2 + table->col_ofs,
711 y2, table->row_ofs, y2 + table->row_ofs,
712 tab_nc (table), tab_nr (table));
717 tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
719 j = pool_alloc (table->container, sizeof *j);
720 j->x1 = x1 + table->col_ofs;
721 j->y1 = y1 + table->row_ofs;
722 j->x2 = ++x2 + table->col_ofs;
723 j->y2 = ++y2 + table->row_ofs;
724 j->contents = ss_cstr (text);
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 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
752 options OPT to have text value TEXT. */
754 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
755 unsigned opt, const char *text)
757 do_tab_joint_text (table, x1, y1, x2, y2, opt,
758 pool_strdup (table->container, text));
761 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them
762 with options OPT to have text value FORMAT, which is formatted
763 as if passed to printf. */
765 tab_joint_text_format (struct tab_table *table, int x1, int y1, int x2, int y2,
766 unsigned opt, const char *format, ...)
770 va_start (args, format);
771 do_tab_joint_text (table, x1, y1, x2, y2, opt,
772 pool_vasprintf (table->container, format, args));
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);
797 do_tab_output_text (struct tab_table *t, int options, char *text)
799 do_tab_text (t, 0, 0, options, text);
800 tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
801 tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL, NULL);
805 /* Outputs TEXT as a table with a single cell having cell options
806 OPTIONS, which is a combination of the TAB_* and TAT_*
809 tab_output_text (int options, const char *text)
811 struct tab_table *table = tab_create (1, 1);
812 do_tab_output_text (table, options, pool_strdup (table->container, text));
815 /* Outputs FORMAT as a table with a single cell having cell
816 options OPTIONS, which is a combination of the TAB_* and TAT_*
817 constants. FORMAT is formatted as if it was passed through
820 tab_output_text_format (int options, const char *format, ...)
822 struct tab_table *table;
825 table = tab_create (1, 1);
827 va_start (args, format);
828 do_tab_output_text (table, options,
829 pool_vasprintf (table->container, format, args));
833 /* Set table flags to FLAGS. */
835 tab_flags (struct tab_table *t, unsigned flags)
841 /* Easy, type-safe way to submit a tab table to som. */
843 tab_submit (struct tab_table *t)
848 s.class = &tab_table_class;
857 /* Set table row and column offsets for all functions that affect
860 tab_offset (struct tab_table *t, int col, int row)
866 if (row < -1 || row > tab_nr (t))
868 printf ("tab_offset(): row=%d in %d-row table\n", row, tab_nr (t));
871 if (col < -1 || col > tab_nc (t))
873 printf ("tab_offset(): col=%d in %d-column table\n", col, tab_nc (t));
879 diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
881 diff += (col - t->col_ofs), t->col_ofs = col;
887 /* Increment the row offset by one. If the table is too small,
888 increase its size. */
890 tab_next_row (struct tab_table *t)
895 if (++t->row_ofs >= tab_nr (t))
896 tab_realloc (t, -1, tab_nr (t) * 4 / 3);
899 /* Return the number of columns and rows in the table into N_COLUMNS
900 and N_ROWS, respectively. */
902 tabi_count (struct som_entity *t_, int *n_columns, int *n_rows)
904 struct tab_table *t = t_->ext;
909 /* Return the column style for this table into STYLE. */
911 tabi_columns (struct som_entity *t_, int *style)
913 struct tab_table *t = t_->ext;
914 *style = t->col_style;
917 /* Return the number of header rows/columns on the left, right, top,
918 and bottom sides into HL, HR, HT, and HB, respectively. */
920 tabi_headers (struct som_entity *t_, int *hl, int *hr, int *ht, int *hb)
922 struct tab_table *t = t_->ext;
929 /* Return flags set for the current table into FLAGS. */
931 tabi_flags (struct som_entity *t_, unsigned *flags)
933 struct tab_table *t = t_->ext;
937 /* Returns the line style to use for spacing purposes for a rule
938 of the given TYPE. */
939 static enum outp_line_style
940 rule_to_spacing_type (unsigned char type)
948 return OUTP_L_SINGLE;
950 return OUTP_L_DOUBLE;
957 tabi_render_init (struct som_entity *t_, struct outp_driver *driver,
958 int hl, int hr, int ht, int hb)
960 const struct tab_table *t = t_->ext;
961 struct tab_rendering *r;
965 tab_offset (t_->ext, 0, 0);
967 r = xmalloc (sizeof *r);
970 r->w = xnmalloc (tab_nc (t), sizeof *r->w);
971 r->h = xnmalloc (tab_nr (t), sizeof *r->h);
972 r->hrh = xnmalloc (tab_nr (t) + 1, sizeof *r->hrh);
973 r->wrv = xnmalloc (tab_nc (t) + 1, sizeof *r->wrv);
979 /* Figure out sizes of rules. */
980 for (row = 0; row <= tab_nr (t); row++)
983 for (col = 0; col < tab_nc (t); col++)
985 unsigned char rh = t->rh[col + row * t->cf];
986 int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
993 for (col = 0; col <= tab_nc (t); col++)
996 for (row = 0; row < tab_nr (t); row++)
998 unsigned char *rv = &t->rv[col + row * (t->cf + 1)];
1000 if (*rv == UCHAR_MAX)
1001 *rv = col != 0 && col != tab_nc (t) ? TAL_GAP : TAL_0;
1002 w = driver->vert_line_width[rule_to_spacing_type (*rv)];
1006 r->wrv[col] = width;
1009 /* Determine row heights and columns widths. */
1010 for (i = 0; i < tab_nr (t); i++)
1012 for (i = 0; i < tab_nc (t); i++)
1015 t->dim (r, t->dim_aux);
1017 for (i = 0; i < tab_nr (t); i++)
1019 error (0, 0, "height of table row %d is %d (not initialized?)",
1021 for (i = 0; i < tab_nc (t); i++)
1023 error (0, 0, "width of table column %d is %d (not initialized?)",
1026 /* Add up header sizes. */
1027 for (i = 0, r->wl = r->wrv[0]; i < r->l; i++)
1028 r->wl += r->w[i] + r->wrv[i + 1];
1029 for (i = 0, r->ht = r->hrh[0]; i < r->t; i++)
1030 r->ht += r->h[i] + r->hrh[i + 1];
1031 for (i = tab_nc (t) - r->r, r->wr = r->wrv[i]; i < tab_nc (t); i++)
1032 r->wr += r->w[i] + r->wrv[i + 1];
1033 for (i = tab_nr (t) - r->b, r->hb = r->hrh[i]; i < tab_nr (t); i++)
1034 r->hb += r->h[i] + r->hrh[i + 1];
1037 if (!(t->flags & SOMF_NO_TITLE))
1038 r->ht += driver->font_height;
1044 tabi_render_free (void *r_)
1046 struct tab_rendering *r = r_;
1055 /* Return the horizontal and vertical size of the entire table,
1056 including headers, for the current output device, into HORIZ and
1059 tabi_area (void *r_, int *horiz, int *vert)
1061 struct tab_rendering *r = r_;
1062 const struct tab_table *t = r->table;
1067 for (col = r->l + 1, width = r->wl + r->wr + r->w[tab_l (t)];
1068 col < tab_nc (t) - r->r; col++)
1069 width += r->w[col] + r->wrv[col];
1073 for (row = r->t + 1, height = r->ht + r->hb + r->h[tab_t (t)];
1074 row < tab_nr (t) - tab_b (t); row++)
1075 height += r->h[row] + r->hrh[row];
1079 /* Determines the number of rows or columns (including appropriate
1080 headers), depending on CUMTYPE, that will fit into the space
1081 specified. Takes rows/columns starting at index START and attempts
1082 to fill up available space MAX. Returns in END the index of the
1083 last row/column plus one; returns in ACTUAL the actual amount of
1084 space the selected rows/columns (including appropriate headers)
1087 tabi_cumulate (void *r_, int cumtype, int start, int *end,
1088 int max, int *actual)
1090 const struct tab_rendering *r = r_;
1091 const struct tab_table *t = r->table;
1097 assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1098 if (cumtype == SOM_ROWS)
1100 assert (start >= 0 && start < tab_nr (t));
1101 limit = tab_nr (t) - r->b;
1102 cells = &r->h[start];
1103 rules = &r->hrh[start + 1];
1104 total = r->ht + r->hb;
1108 assert (start >= 0 && start < tab_nc (t));
1109 limit = tab_nc (t) - tab_r (t);
1110 cells = &r->w[start];
1111 rules = &r->wrv[start + 1];
1112 total = r->wl + r->wr;
1125 for (idx = start + 1; idx < limit; idx++)
1127 int amt = *cells++ + *rules++;
1144 /* Render title for current table, with major index X and minor index
1145 Y. Y may be zero, or X and Y may be zero, but X should be nonzero
1148 tabi_title (void *r_, int x, int y, int table_num, int subtable_num,
1149 const char *command_name)
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, tab_t (t) * 2 + 1);
1214 add_range (rows, &n_row_ranges, r0 * 2 + 1, r1 * 2);
1215 add_range (rows, &n_row_ranges, (tab_nr (t) - tab_b (t)) * 2,
1216 tab_nr (t) * 2 + 1);
1218 /* Columns to render, counting vertical rules as columns. */
1220 add_range (cols, &n_col_ranges, 0, r->l * 2 + 1);
1221 add_range (cols, &n_col_ranges, c0 * 2 + 1, c1 * 2);
1222 add_range (cols, &n_col_ranges, (tab_nc (t) - r->r) * 2, tab_nc (t) * 2 + 1);
1224 y = r->driver->cp_y;
1225 if (!(t->flags & SOMF_NO_TITLE))
1226 y += r->driver->font_height;
1227 for (i = 0; i < n_row_ranges; i++)
1231 for (row = rows[i][0]; row < rows[i][1]; row++)
1235 x = r->driver->cp_x;
1236 for (j = 0; j < n_col_ranges; j++)
1237 x = render_strip (r, x, y, row,
1238 cols[j][0], cols[j][1],
1239 rows[i][0], rows[i][1]);
1241 y += (row & 1) ? r->h[row / 2] : r->hrh[row / 2];
1246 const struct som_table_class tab_table_class =
1262 static enum outp_justification
1263 translate_justification (unsigned int opt)
1265 switch (opt & TAB_ALIGN_MASK)
1278 /* Returns the line style to use for drawing a rule of the given
1280 static enum outp_line_style
1281 rule_to_draw_type (unsigned char type)
1289 return OUTP_L_SINGLE;
1291 return OUTP_L_DOUBLE;
1297 /* Returns the horizontal rule at the given column and row. */
1299 get_hrule (const struct tab_table *t, int col, int row)
1301 return t->rh[col + row * t->cf];
1304 /* Returns the vertical rule at the given column and row. */
1306 get_vrule (const struct tab_table *t, int col, int row)
1308 return t->rv[col + row * (t->cf + 1)];
1311 /* Renders the horizontal rule at the given column and row
1312 at (X,Y) on the page. */
1314 render_horz_rule (const struct tab_rendering *r,
1315 int x, int y, int col, int row)
1317 enum outp_line_style style;
1318 style = rule_to_draw_type (get_hrule (r->table, col, row));
1319 if (style != OUTP_L_NONE)
1320 r->driver->class->line (r->driver, x, y, x + r->w[col], y + r->hrh[row],
1321 OUTP_L_NONE, style, OUTP_L_NONE, style);
1324 /* Renders the vertical rule at the given column and row
1325 at (X,Y) on the page. */
1327 render_vert_rule (const struct tab_rendering *r,
1328 int x, int y, int col, int row)
1330 enum outp_line_style style;
1331 style = rule_to_draw_type (get_vrule (r->table, col, row));
1332 if (style != OUTP_L_NONE)
1333 r->driver->class->line (r->driver, x, y, x + r->wrv[col], y + r->h[row],
1334 style, OUTP_L_NONE, style, OUTP_L_NONE);
1337 /* Renders the rule intersection at the given column and row
1338 at (X,Y) on the page. */
1340 render_rule_intersection (const struct tab_rendering *r,
1341 int x, int y, int col, int row)
1343 const struct tab_table *t = r->table;
1345 /* Bounds of intersection. */
1348 int x1 = x + r->wrv[col];
1349 int y1 = y + r->hrh[row];
1351 /* Lines on each side of intersection. */
1352 int top = row > 0 ? get_vrule (t, col, row - 1) : TAL_0;
1353 int left = col > 0 ? get_hrule (t, col - 1, row) : TAL_0;
1354 int bottom = row < tab_nr (t) ? get_vrule (t, col, row) : TAL_0;
1355 int right = col < tab_nc (t) ? get_hrule (t, col, row) : TAL_0;
1357 /* Output style for each line. */
1358 enum outp_line_style o_top = rule_to_draw_type (top);
1359 enum outp_line_style o_left = rule_to_draw_type (left);
1360 enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1361 enum outp_line_style o_right = rule_to_draw_type (right);
1363 if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1364 || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1365 r->driver->class->line (r->driver, x0, y0, x1, y1,
1366 o_top, o_left, o_bottom, o_right);
1369 /* Returns the width of columns C1...C2 exclusive,
1370 including interior but not exterior rules. */
1372 strip_width (const struct tab_rendering *r, int c1, int c2)
1377 for (c = c1; c < c2; c++)
1378 width += r->w[c] + r->wrv[c + 1];
1380 width -= r->wrv[c2];
1384 /* Returns the height of rows R1...R2 exclusive,
1385 including interior but not exterior rules. */
1387 strip_height (const struct tab_rendering *r, int r1, int r2)
1392 for (row = r1; row < r2; row++)
1393 height += r->h[row] + r->hrh[row + 1];
1395 height -= r->hrh[r2];
1399 /* Renders the cell at the given column and row at (X,Y) on the
1400 page. Also renders joined cells that extend as far to the
1401 right as C1 and as far down as R1. */
1403 render_cell (const struct tab_rendering *r,
1404 int x, int y, int col, int row, int c1, int r1)
1406 const struct tab_table *t = r->table;
1407 const int index = col + (row * t->cf);
1408 unsigned char type = t->ct[index];
1409 struct substring *content = &t->cc[index];
1411 if (!(type & TAB_JOIN))
1413 if (!(type & TAB_EMPTY))
1415 struct outp_text text;
1416 text.font = options_to_font (type);
1417 text.justification = translate_justification (type);
1418 text.string = *content;
1423 r->driver->class->text_draw (r->driver, &text);
1428 struct tab_joined_cell *j
1429 = (struct tab_joined_cell *) ss_data (*content);
1431 if (j->x1 == col && j->y1 == row)
1433 struct outp_text text;
1434 text.font = options_to_font (type);
1435 text.justification = translate_justification (type);
1436 text.string = j->contents;
1439 text.h = strip_width (r, j->x1, MIN (j->x2, c1));
1440 text.v = strip_height (r, j->y1, MIN (j->y2, r1));
1441 r->driver->class->text_draw (r->driver, &text);
1446 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1447 on row ROW, at (X,Y). Returns X position after rendering.
1448 Also renders joined cells that extend beyond that strip,
1449 cropping them to lie within rendering region (C0,R0)-(C1,R1).
1450 C0 and C1 count vertical rules as columns.
1451 ROW counts horizontal rules as rows, but R0 and R1 do not. */
1453 render_strip (const struct tab_rendering *r,
1454 int x, int y, int row, int c0, int c1, int r0 UNUSED, int r1)
1458 for (col = c0; col < c1; col++)
1462 render_cell (r, x, y, col / 2, row / 2, c1 / 2, r1);
1464 render_horz_rule (r, x, y, col / 2, row / 2);
1470 render_vert_rule (r, x, y, col / 2, row / 2);
1472 render_rule_intersection (r, x, y, col / 2, row / 2);
1473 x += r->wrv[col / 2];