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 /* Set the title of table T to TITLE, which is formatted as if
386 passed to printf(). */
388 tab_title (struct tab_table *t, const char *title, ...)
392 assert (t != NULL && title != NULL);
393 va_start (args, title);
394 t->title = xvasprintf (title, args);
398 /* Set DIM_FUNC, which will be passed auxiliary data AUX, as the
399 dimension function for table T.
401 DIM_FUNC must not assume that it is called from the same
402 context as tab_dim; for example, table T might be kept in
403 memory and, thus, DIM_FUNC might be called after the currently
404 running command completes. If it is non-null, FREE_FUNC is
405 called when the table is destroyed, to allow any data
406 allocated for use by DIM_FUNC to be freed. */
408 tab_dim (struct tab_table *t,
409 tab_dim_func *dim_func, tab_dim_free_func *free_func, void *aux)
411 assert (t->dim == NULL);
413 t->dim_free = free_func;
417 /* Returns the natural width of column C in table T for driver D, that
418 is, the smallest width necessary to display all its cells without
419 wrapping. The width will be no larger than the page width minus
420 left and right rule widths. */
422 tab_natural_width (const struct tab_rendering *r, int col)
424 const struct tab_table *t = r->table;
425 int width, row, max_width;
427 assert (col >= 0 && col < tab_nc (t));
430 for (row = 0; row < tab_nr (t); row++)
432 struct outp_text text;
433 unsigned char opt = t->ct[col + row * t->cf];
436 if (opt & (TAB_JOIN | TAB_EMPTY))
439 text.string = t->cc[col + row * t->cf];
440 text.justification = OUTP_LEFT;
441 text.font = options_to_font (opt);
442 text.h = text.v = INT_MAX;
444 r->driver->class->text_metrics (r->driver, &text, &w, NULL);
451 /* FIXME: This is an ugly kluge to compensate for the fact
452 that we don't let joined cells contribute to column
454 width = r->driver->prop_em_width * 8;
457 max_width = r->driver->width - r->wrv[0] - r->wrv[tab_nc (t)];
458 return MIN (width, max_width);
461 /* Returns the natural height of row R in table T for driver D, that
462 is, the minimum height necessary to display the information in the
463 cell at the widths set for each column. */
465 tab_natural_height (const struct tab_rendering *r, int row)
467 const struct tab_table *t = r->table;
470 assert (row >= 0 && row < tab_nr (t));
472 height = r->driver->font_height;
473 for (col = 0; col < tab_nc (t); col++)
475 struct outp_text text;
476 unsigned char opt = t->ct[col + row * t->cf];
479 if (opt & (TAB_JOIN | TAB_EMPTY))
482 text.string = t->cc[col + row * t->cf];
483 text.justification = OUTP_LEFT;
484 text.font = options_to_font (opt);
487 r->driver->class->text_metrics (r->driver, &text, NULL, &h);
496 /* Callback function to set all columns and rows to their natural
497 dimensions. Not really meant to be called directly. */
499 tab_natural_dimensions (struct tab_rendering *r, void *aux UNUSED)
501 const struct tab_table *t = r->table;
504 for (i = 0; i < tab_nc (t); i++)
505 r->w[i] = tab_natural_width (r, i);
507 for (i = 0; i < tab_nr (t); i++)
508 r->h[i] = tab_natural_height (r, i);
514 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
515 from V, displayed with format spec F. */
517 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
518 const union value *v, const struct fmt_spec *f)
522 assert (table != NULL && v != NULL && f != NULL);
524 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
525 || c + table->col_ofs >= tab_nc (table)
526 || r + table->row_ofs >= tab_nr (table))
528 printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
530 c, table->col_ofs, c + table->col_ofs,
531 r, table->row_ofs, r + table->row_ofs,
532 tab_nc (table), tab_nr (table));
537 contents = pool_alloc (table->container, f->w);
538 table->cc[c + r * table->cf] = ss_buffer (contents, f->w);
539 table->ct[c + r * table->cf] = opt;
541 data_out (v, f, contents);
544 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
545 with NDEC decimal places. */
547 tab_fixed (struct tab_table *table, int c, int r, unsigned char opt,
548 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 data_out (&double_value, &f, buf);
583 while (isspace ((unsigned char) *cp) && cp < &buf[w])
585 f.w = w - (cp - buf);
587 contents = pool_alloc (table->container, f.w);
588 table->cc[c + r * table->cf] = ss_buffer (contents, f.w);
589 table->ct[c + r * table->cf] = opt;
590 memcpy (contents, cp, f.w);
593 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL as
595 If FMT is null, then the default print format will be used.
598 tab_double (struct tab_table *table, int c, int r, unsigned char opt,
599 double val, const struct fmt_spec *fmt)
605 union value double_value;
607 assert (table != NULL);
610 assert (c < tab_nc (table));
612 assert (r < tab_nr (table));
615 fmt = settings_get_format ();
617 fmt_check_output (fmt);
620 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
621 || c + table->col_ofs >= tab_nc (table)
622 || r + table->row_ofs >= tab_nr (table))
624 printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
626 c, table->col_ofs, c + table->col_ofs,
627 r, table->row_ofs, r + table->row_ofs,
628 tab_nc (table), tab_nr (table));
633 double_value.f = val;
634 data_out (&double_value, fmt, buf);
637 while (isspace ((unsigned char) *cp) && cp < &buf[fmt->w])
639 w = fmt->w - (cp - buf);
641 contents = pool_alloc (table->container, w);
642 table->cc[c + r * table->cf] = ss_buffer (contents, w);
643 table->ct[c + r * table->cf] = opt;
644 memcpy (contents, cp, w);
649 do_tab_text (struct tab_table *table, int c, int r, unsigned opt, char *text)
653 assert (c < tab_nc (table));
654 assert (r < tab_nr (table));
657 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
658 || c + table->col_ofs >= tab_nc (table)
659 || r + table->row_ofs >= tab_nr (table))
661 printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
663 c, table->col_ofs, c + table->col_ofs,
664 r, table->row_ofs, r + table->row_ofs,
665 tab_nc (table), tab_nr (table));
670 table->cc[c + r * table->cf] = ss_cstr (text);
671 table->ct[c + r * table->cf] = opt;
674 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
677 tab_text (struct tab_table *table, int c, int r, unsigned opt,
680 do_tab_text (table, c, r, opt, pool_strdup (table->container, text));
683 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
684 FORMAT, which is formatted as if passed to printf. */
686 tab_text_format (struct tab_table *table, int c, int r, unsigned opt,
687 const char *format, ...)
691 va_start (args, format);
692 do_tab_text (table, c, r, opt,
693 pool_vasprintf (table->container, format, args));
698 do_tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
699 unsigned opt, char *text)
701 struct tab_joined_cell *j;
703 assert (x1 + table->col_ofs >= 0);
704 assert (y1 + table->row_ofs >= 0);
707 assert (y2 + table->row_ofs < tab_nr (table));
708 assert (x2 + table->col_ofs < tab_nc (table));
711 if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= tab_nc (table)
712 || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= tab_nr (table)
713 || x2 < x1 || x2 + table->col_ofs >= tab_nc (table)
714 || y2 < y2 || y2 + table->row_ofs >= tab_nr (table))
716 printf ("tab_joint_text(): bad cell "
717 "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
718 x1, table->col_ofs, x1 + table->col_ofs,
719 y1, table->row_ofs, y1 + table->row_ofs,
720 x2, table->col_ofs, x2 + table->col_ofs,
721 y2, table->row_ofs, y2 + table->row_ofs,
722 tab_nc (table), tab_nr (table));
727 tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
729 j = pool_alloc (table->container, sizeof *j);
730 j->x1 = x1 + table->col_ofs;
731 j->y1 = y1 + table->row_ofs;
732 j->x2 = ++x2 + table->col_ofs;
733 j->y2 = ++y2 + table->row_ofs;
734 j->contents = ss_cstr (text);
739 struct substring *cc = &table->cc[x1 + y1 * table->cf];
740 unsigned char *ct = &table->ct[x1 + y1 * table->cf];
741 const int ofs = table->cf - (x2 - x1);
745 for (y = y1; y < y2; y++)
749 for (x = x1; x < x2; x++)
751 *cc++ = ss_buffer ((char *) j, 0);
761 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
762 options OPT to have text value TEXT. */
764 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
765 unsigned opt, const char *text)
767 do_tab_joint_text (table, x1, y1, x2, y2, opt,
768 pool_strdup (table->container, text));
771 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them
772 with options OPT to have text value FORMAT, which is formatted
773 as if passed to printf. */
775 tab_joint_text_format (struct tab_table *table, int x1, int y1, int x2, int y2,
776 unsigned opt, const char *format, ...)
780 va_start (args, format);
781 do_tab_joint_text (table, x1, y1, x2, y2, opt,
782 pool_vasprintf (table->container, format, args));
786 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
788 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
789 struct substring *string)
791 assert (table != NULL && string != NULL);
794 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
795 || c + table->col_ofs >= tab_nc (table)
796 || r + table->row_ofs >= tab_nr (table))
798 printf ("tab_raw(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
800 c, table->col_ofs, c + table->col_ofs,
801 r, table->row_ofs, r + table->row_ofs,
802 tab_nc (table), tab_nr (table));
807 table->cc[c + r * table->cf] = *string;
808 table->ct[c + r * table->cf] = opt;
813 /* Sets the widths of all the columns and heights of all the rows in
814 table T for driver D. */
816 nowrap_dim (struct tab_rendering *r, void *aux UNUSED)
818 r->w[0] = tab_natural_width (r, 0);
819 r->h[0] = r->driver->font_height;
822 /* Sets the widths of all the columns and heights of all the rows in
823 table T for driver D. */
825 wrap_dim (struct tab_rendering *r, void *aux UNUSED)
827 r->w[0] = tab_natural_width (r, 0);
828 r->h[0] = tab_natural_height (r, 0);
832 do_tab_output_text (struct tab_table *t, int options, char *text)
834 do_tab_text (t, 0, 0, options, text);
835 tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
836 tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL, NULL);
840 /* Outputs TEXT as a table with a single cell having cell options
841 OPTIONS, which is a combination of the TAB_* and TAT_*
844 tab_output_text (int options, const char *text)
846 struct tab_table *table = tab_create (1, 1, 0);
847 do_tab_output_text (table, options, pool_strdup (table->container, text));
850 /* Outputs FORMAT as a table with a single cell having cell
851 options OPTIONS, which is a combination of the TAB_* and TAT_*
852 constants. FORMAT is formatted as if it was passed through
855 tab_output_text_format (int options, const char *format, ...)
857 struct tab_table *table;
860 table = tab_create (1, 1, 0);
862 va_start (args, format);
863 do_tab_output_text (table, options,
864 pool_vasprintf (table->container, format, args));
868 /* Set table flags to FLAGS. */
870 tab_flags (struct tab_table *t, unsigned flags)
876 /* Easy, type-safe way to submit a tab table to som. */
878 tab_submit (struct tab_table *t)
883 s.class = &tab_table_class;
892 /* Set table row and column offsets for all functions that affect
895 tab_offset (struct tab_table *t, int col, int row)
901 if (row < -1 || row > tab_nr (t))
903 printf ("tab_offset(): row=%d in %d-row table\n", row, tab_nr (t));
906 if (col < -1 || col > tab_nc (t))
908 printf ("tab_offset(): col=%d in %d-column table\n", col, tab_nc (t));
914 diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
916 diff += (col - t->col_ofs), t->col_ofs = col;
922 /* Increment the row offset by one. If the table is too small,
923 increase its size. */
925 tab_next_row (struct tab_table *t)
930 if (++t->row_ofs >= tab_nr (t))
931 tab_realloc (t, -1, tab_nr (t) * 4 / 3);
934 /* Return the number of columns and rows in the table into N_COLUMNS
935 and N_ROWS, respectively. */
937 tabi_count (struct som_entity *t_, int *n_columns, int *n_rows)
939 struct tab_table *t = t_->ext;
944 /* Return the column style for this table into STYLE. */
946 tabi_columns (struct som_entity *t_, int *style)
948 struct tab_table *t = t_->ext;
949 *style = t->col_style;
952 /* Return the number of header rows/columns on the left, right, top,
953 and bottom sides into HL, HR, HT, and HB, respectively. */
955 tabi_headers (struct som_entity *t_, int *hl, int *hr, int *ht, int *hb)
957 struct tab_table *t = t_->ext;
964 /* Return flags set for the current table into FLAGS. */
966 tabi_flags (struct som_entity *t_, unsigned *flags)
968 struct tab_table *t = t_->ext;
972 /* Returns the line style to use for spacing purposes for a rule
973 of the given TYPE. */
974 static enum outp_line_style
975 rule_to_spacing_type (unsigned char type)
983 return OUTP_L_SINGLE;
985 return OUTP_L_DOUBLE;
992 tabi_render_init (struct som_entity *t_, struct outp_driver *driver,
993 int hl, int hr, int ht, int hb)
995 const struct tab_table *t = t_->ext;
996 struct tab_rendering *r;
1000 tab_offset (t_->ext, 0, 0);
1002 r = xmalloc (sizeof *r);
1005 r->w = xnmalloc (tab_nc (t), sizeof *r->w);
1006 r->h = xnmalloc (tab_nr (t), sizeof *r->h);
1007 r->hrh = xnmalloc (tab_nr (t) + 1, sizeof *r->hrh);
1008 r->wrv = xnmalloc (tab_nc (t) + 1, sizeof *r->wrv);
1014 /* Figure out sizes of rules. */
1015 for (row = 0; row <= tab_nr (t); row++)
1018 for (col = 0; col < tab_nc (t); col++)
1020 unsigned char rh = t->rh[col + row * t->cf];
1021 int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
1025 r->hrh[row] = width;
1028 for (col = 0; col <= tab_nc (t); col++)
1031 for (row = 0; row < tab_nr (t); row++)
1033 unsigned char *rv = &t->rv[col + row * (t->cf + 1)];
1035 if (*rv == UCHAR_MAX)
1036 *rv = col != 0 && col != tab_nc (t) ? TAL_GAP : TAL_0;
1037 w = driver->vert_line_width[rule_to_spacing_type (*rv)];
1041 r->wrv[col] = width;
1044 /* Determine row heights and columns widths. */
1045 for (i = 0; i < tab_nr (t); i++)
1047 for (i = 0; i < tab_nc (t); i++)
1050 t->dim (r, t->dim_aux);
1052 for (i = 0; i < tab_nr (t); i++)
1054 error (0, 0, "height of table row %d is %d (not initialized?)",
1056 for (i = 0; i < tab_nc (t); i++)
1058 error (0, 0, "width of table column %d is %d (not initialized?)",
1061 /* Add up header sizes. */
1062 for (i = 0, r->wl = r->wrv[0]; i < r->l; i++)
1063 r->wl += r->w[i] + r->wrv[i + 1];
1064 for (i = 0, r->ht = r->hrh[0]; i < r->t; i++)
1065 r->ht += r->h[i] + r->hrh[i + 1];
1066 for (i = tab_nc (t) - r->r, r->wr = r->wrv[i]; i < tab_nc (t); i++)
1067 r->wr += r->w[i] + r->wrv[i + 1];
1068 for (i = tab_nr (t) - r->b, r->hb = r->hrh[i]; i < tab_nr (t); i++)
1069 r->hb += r->h[i] + r->hrh[i + 1];
1072 if (!(t->flags & SOMF_NO_TITLE))
1073 r->ht += driver->font_height;
1079 tabi_render_free (void *r_)
1081 struct tab_rendering *r = r_;
1090 /* Return the horizontal and vertical size of the entire table,
1091 including headers, for the current output device, into HORIZ and
1094 tabi_area (void *r_, int *horiz, int *vert)
1096 struct tab_rendering *r = r_;
1097 const struct tab_table *t = r->table;
1102 for (col = r->l + 1, width = r->wl + r->wr + r->w[tab_l (t)];
1103 col < tab_nc (t) - r->r; col++)
1104 width += r->w[col] + r->wrv[col];
1108 for (row = r->t + 1, height = r->ht + r->hb + r->h[tab_t (t)];
1109 row < tab_nr (t) - tab_b (t); row++)
1110 height += r->h[row] + r->hrh[row];
1114 /* Determines the number of rows or columns (including appropriate
1115 headers), depending on CUMTYPE, that will fit into the space
1116 specified. Takes rows/columns starting at index START and attempts
1117 to fill up available space MAX. Returns in END the index of the
1118 last row/column plus one; returns in ACTUAL the actual amount of
1119 space the selected rows/columns (including appropriate headers)
1122 tabi_cumulate (void *r_, int cumtype, int start, int *end,
1123 int max, int *actual)
1125 const struct tab_rendering *r = r_;
1126 const struct tab_table *t = r->table;
1132 assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1133 if (cumtype == SOM_ROWS)
1135 assert (start >= 0 && start < tab_nr (t));
1136 limit = tab_nr (t) - r->b;
1137 cells = &r->h[start];
1138 rules = &r->hrh[start + 1];
1139 total = r->ht + r->hb;
1143 assert (start >= 0 && start < tab_nc (t));
1144 limit = tab_nc (t) - tab_r (t);
1145 cells = &r->w[start];
1146 rules = &r->wrv[start + 1];
1147 total = r->wl + r->wr;
1160 for (idx = start + 1; idx < limit; idx++)
1162 int amt = *cells++ + *rules++;
1179 /* Render title for current table, with major index X and minor index
1180 Y. Y may be zero, or X and Y may be zero, but X should be nonzero
1183 tabi_title (void *r_, int x, int y, int table_num, int subtable_num,
1184 const char *command_name)
1186 const struct tab_rendering *r = r_;
1187 const struct tab_table *t = r->table;
1188 struct outp_text text;
1189 struct string title;
1191 if (t->flags & SOMF_NO_TITLE)
1194 ds_init_empty (&title);
1195 ds_put_format (&title,"%d.%d", table_num, subtable_num);
1197 ds_put_format (&title, "(%d:%d)", x, y);
1199 ds_put_format (&title, "(%d)", x);
1200 if (command_name != NULL)
1201 ds_put_format (&title, " %s", command_name);
1202 ds_put_cstr (&title, ". ");
1203 if (t->title != NULL)
1204 ds_put_cstr (&title, t->title);
1206 text.font = OUTP_PROPORTIONAL;
1207 text.justification = OUTP_LEFT;
1208 text.string = ds_ss (&title);
1209 text.h = r->driver->width;
1210 text.v = r->driver->font_height;
1212 text.y = r->driver->cp_y;
1213 r->driver->class->text_draw (r->driver, &text);
1215 ds_destroy (&title);
1218 static int render_strip (const struct tab_rendering *,
1219 int x, int y, int r, int c1, int c2, int r1, int r2);
1222 add_range (int ranges[][2], int *np, int start, int end)
1225 if (n == 0 || start > ranges[n - 1][1])
1227 ranges[n][0] = start;
1232 ranges[n - 1][1] = end;
1235 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1236 current position on the current output device. */
1238 tabi_render (void *r_, int c0, int r0, int c1, int r1)
1240 const struct tab_rendering *r = r_;
1241 const struct tab_table *t = r->table;
1242 int rows[3][2], cols[3][2];
1243 int n_row_ranges, n_col_ranges;
1246 /* Rows to render, counting horizontal rules as rows. */
1248 add_range (rows, &n_row_ranges, 0, tab_t (t) * 2 + 1);
1249 add_range (rows, &n_row_ranges, r0 * 2 + 1, r1 * 2);
1250 add_range (rows, &n_row_ranges, (tab_nr (t) - tab_b (t)) * 2,
1251 tab_nr (t) * 2 + 1);
1253 /* Columns to render, counting vertical rules as columns. */
1255 add_range (cols, &n_col_ranges, 0, r->l * 2 + 1);
1256 add_range (cols, &n_col_ranges, c0 * 2 + 1, c1 * 2);
1257 add_range (cols, &n_col_ranges, (tab_nc (t) - r->r) * 2, tab_nc (t) * 2 + 1);
1259 y = r->driver->cp_y;
1260 if (!(t->flags & SOMF_NO_TITLE))
1261 y += r->driver->font_height;
1262 for (i = 0; i < n_row_ranges; i++)
1266 for (row = rows[i][0]; row < rows[i][1]; row++)
1270 x = r->driver->cp_x;
1271 for (j = 0; j < n_col_ranges; j++)
1272 x = render_strip (r, x, y, row,
1273 cols[j][0], cols[j][1],
1274 rows[i][0], rows[i][1]);
1276 y += (row & 1) ? r->h[row / 2] : r->hrh[row / 2];
1281 const struct som_table_class tab_table_class =
1297 static enum outp_justification
1298 translate_justification (unsigned int opt)
1300 switch (opt & TAB_ALIGN_MASK)
1313 /* Returns the line style to use for drawing a rule of the given
1315 static enum outp_line_style
1316 rule_to_draw_type (unsigned char type)
1324 return OUTP_L_SINGLE;
1326 return OUTP_L_DOUBLE;
1332 /* Returns the horizontal rule at the given column and row. */
1334 get_hrule (const struct tab_table *t, int col, int row)
1336 return t->rh[col + row * t->cf];
1339 /* Returns the vertical rule at the given column and row. */
1341 get_vrule (const struct tab_table *t, int col, int row)
1343 return t->rv[col + row * (t->cf + 1)];
1346 /* Renders the horizontal rule at the given column and row
1347 at (X,Y) on the page. */
1349 render_horz_rule (const struct tab_rendering *r,
1350 int x, int y, int col, int row)
1352 enum outp_line_style style;
1353 style = rule_to_draw_type (get_hrule (r->table, col, row));
1354 if (style != OUTP_L_NONE)
1355 r->driver->class->line (r->driver, x, y, x + r->w[col], y + r->hrh[row],
1356 OUTP_L_NONE, style, OUTP_L_NONE, style);
1359 /* Renders the vertical rule at the given column and row
1360 at (X,Y) on the page. */
1362 render_vert_rule (const struct tab_rendering *r,
1363 int x, int y, int col, int row)
1365 enum outp_line_style style;
1366 style = rule_to_draw_type (get_vrule (r->table, col, row));
1367 if (style != OUTP_L_NONE)
1368 r->driver->class->line (r->driver, x, y, x + r->wrv[col], y + r->h[row],
1369 style, OUTP_L_NONE, style, OUTP_L_NONE);
1372 /* Renders the rule intersection at the given column and row
1373 at (X,Y) on the page. */
1375 render_rule_intersection (const struct tab_rendering *r,
1376 int x, int y, int col, int row)
1378 const struct tab_table *t = r->table;
1380 /* Bounds of intersection. */
1383 int x1 = x + r->wrv[col];
1384 int y1 = y + r->hrh[row];
1386 /* Lines on each side of intersection. */
1387 int top = row > 0 ? get_vrule (t, col, row - 1) : TAL_0;
1388 int left = col > 0 ? get_hrule (t, col - 1, row) : TAL_0;
1389 int bottom = row < tab_nr (t) ? get_vrule (t, col, row) : TAL_0;
1390 int right = col < tab_nc (t) ? get_hrule (t, col, row) : TAL_0;
1392 /* Output style for each line. */
1393 enum outp_line_style o_top = rule_to_draw_type (top);
1394 enum outp_line_style o_left = rule_to_draw_type (left);
1395 enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1396 enum outp_line_style o_right = rule_to_draw_type (right);
1398 if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1399 || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1400 r->driver->class->line (r->driver, x0, y0, x1, y1,
1401 o_top, o_left, o_bottom, o_right);
1404 /* Returns the width of columns C1...C2 exclusive,
1405 including interior but not exterior rules. */
1407 strip_width (const struct tab_rendering *r, int c1, int c2)
1412 for (c = c1; c < c2; c++)
1413 width += r->w[c] + r->wrv[c + 1];
1415 width -= r->wrv[c2];
1419 /* Returns the height of rows R1...R2 exclusive,
1420 including interior but not exterior rules. */
1422 strip_height (const struct tab_rendering *r, int r1, int r2)
1427 for (row = r1; row < r2; row++)
1428 height += r->h[row] + r->hrh[row + 1];
1430 height -= r->hrh[r2];
1434 /* Renders the cell at the given column and row at (X,Y) on the
1435 page. Also renders joined cells that extend as far to the
1436 right as C1 and as far down as R1. */
1438 render_cell (const struct tab_rendering *r,
1439 int x, int y, int col, int row, int c1, int r1)
1441 const struct tab_table *t = r->table;
1442 const int index = col + (row * t->cf);
1443 unsigned char type = t->ct[index];
1444 struct substring *content = &t->cc[index];
1446 if (!(type & TAB_JOIN))
1448 if (!(type & TAB_EMPTY))
1450 struct outp_text text;
1451 text.font = options_to_font (type);
1452 text.justification = translate_justification (type);
1453 text.string = *content;
1458 r->driver->class->text_draw (r->driver, &text);
1463 struct tab_joined_cell *j
1464 = (struct tab_joined_cell *) ss_data (*content);
1466 if (j->x1 == col && j->y1 == row)
1468 struct outp_text text;
1469 text.font = options_to_font (type);
1470 text.justification = translate_justification (type);
1471 text.string = j->contents;
1474 text.h = strip_width (r, j->x1, MIN (j->x2, c1));
1475 text.v = strip_height (r, j->y1, MIN (j->y2, r1));
1476 r->driver->class->text_draw (r->driver, &text);
1481 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1482 on row ROW, at (X,Y). Returns X position after rendering.
1483 Also renders joined cells that extend beyond that strip,
1484 cropping them to lie within rendering region (C0,R0)-(C1,R1).
1485 C0 and C1 count vertical rules as columns.
1486 ROW counts horizontal rules as rows, but R0 and R1 do not. */
1488 render_strip (const struct tab_rendering *r,
1489 int x, int y, int row, int c0, int c1, int r0 UNUSED, int r1)
1493 for (col = c0; col < c1; col++)
1497 render_cell (r, x, y, col / 2, row / 2, c1 / 2, r1);
1499 render_horz_rule (r, x, y, col / 2, row / 2);
1505 render_vert_rule (r, x, y, col / 2, row / 2);
1507 render_rule_intersection (r, x, y, col / 2, row / 2);
1508 x += r->wrv[col / 2];