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)
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));
788 /* Sets the widths of all the columns and heights of all the rows in
789 table T for driver D. */
791 nowrap_dim (struct tab_rendering *r, void *aux UNUSED)
793 r->w[0] = tab_natural_width (r, 0);
794 r->h[0] = r->driver->font_height;
797 /* Sets the widths of all the columns and heights of all the rows in
798 table T for driver D. */
800 wrap_dim (struct tab_rendering *r, void *aux UNUSED)
802 r->w[0] = tab_natural_width (r, 0);
803 r->h[0] = tab_natural_height (r, 0);
807 do_tab_output_text (struct tab_table *t, int options, char *text)
809 do_tab_text (t, 0, 0, options, text);
810 tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
811 tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL, NULL);
815 /* Outputs TEXT 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 *text)
821 struct tab_table *table = tab_create (1, 1);
822 do_tab_output_text (table, options, pool_strdup (table->container, text));
825 /* Outputs FORMAT as a table with a single cell having cell
826 options OPTIONS, which is a combination of the TAB_* and TAT_*
827 constants. FORMAT is formatted as if it was passed through
830 tab_output_text_format (int options, const char *format, ...)
832 struct tab_table *table;
835 table = tab_create (1, 1);
837 va_start (args, format);
838 do_tab_output_text (table, options,
839 pool_vasprintf (table->container, format, args));
843 /* Set table flags to FLAGS. */
845 tab_flags (struct tab_table *t, unsigned flags)
851 /* Easy, type-safe way to submit a tab table to som. */
853 tab_submit (struct tab_table *t)
858 s.class = &tab_table_class;
867 /* Set table row and column offsets for all functions that affect
870 tab_offset (struct tab_table *t, int col, int row)
876 if (row < -1 || row > tab_nr (t))
878 printf ("tab_offset(): row=%d in %d-row table\n", row, tab_nr (t));
881 if (col < -1 || col > tab_nc (t))
883 printf ("tab_offset(): col=%d in %d-column table\n", col, tab_nc (t));
889 diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
891 diff += (col - t->col_ofs), t->col_ofs = col;
897 /* Increment the row offset by one. If the table is too small,
898 increase its size. */
900 tab_next_row (struct tab_table *t)
905 if (++t->row_ofs >= tab_nr (t))
906 tab_realloc (t, -1, tab_nr (t) * 4 / 3);
909 /* Return the number of columns and rows in the table into N_COLUMNS
910 and N_ROWS, respectively. */
912 tabi_count (struct som_entity *t_, int *n_columns, int *n_rows)
914 struct tab_table *t = t_->ext;
919 /* Return the column style for this table into STYLE. */
921 tabi_columns (struct som_entity *t_, int *style)
923 struct tab_table *t = t_->ext;
924 *style = t->col_style;
927 /* Return the number of header rows/columns on the left, right, top,
928 and bottom sides into HL, HR, HT, and HB, respectively. */
930 tabi_headers (struct som_entity *t_, int *hl, int *hr, int *ht, int *hb)
932 struct tab_table *t = t_->ext;
939 /* Return flags set for the current table into FLAGS. */
941 tabi_flags (struct som_entity *t_, unsigned *flags)
943 struct tab_table *t = t_->ext;
947 /* Returns the line style to use for spacing purposes for a rule
948 of the given TYPE. */
949 static enum outp_line_style
950 rule_to_spacing_type (unsigned char type)
958 return OUTP_L_SINGLE;
960 return OUTP_L_DOUBLE;
967 tabi_render_init (struct som_entity *t_, struct outp_driver *driver,
968 int hl, int hr, int ht, int hb)
970 const struct tab_table *t = t_->ext;
971 struct tab_rendering *r;
975 tab_offset (t_->ext, 0, 0);
977 r = xmalloc (sizeof *r);
980 r->w = xnmalloc (tab_nc (t), sizeof *r->w);
981 r->h = xnmalloc (tab_nr (t), sizeof *r->h);
982 r->hrh = xnmalloc (tab_nr (t) + 1, sizeof *r->hrh);
983 r->wrv = xnmalloc (tab_nc (t) + 1, sizeof *r->wrv);
989 /* Figure out sizes of rules. */
990 for (row = 0; row <= tab_nr (t); row++)
993 for (col = 0; col < tab_nc (t); col++)
995 unsigned char rh = t->rh[col + row * t->cf];
996 int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
1000 r->hrh[row] = width;
1003 for (col = 0; col <= tab_nc (t); col++)
1006 for (row = 0; row < tab_nr (t); row++)
1008 unsigned char *rv = &t->rv[col + row * (t->cf + 1)];
1010 if (*rv == UCHAR_MAX)
1011 *rv = col != 0 && col != tab_nc (t) ? TAL_GAP : TAL_0;
1012 w = driver->vert_line_width[rule_to_spacing_type (*rv)];
1016 r->wrv[col] = width;
1019 /* Determine row heights and columns widths. */
1020 for (i = 0; i < tab_nr (t); i++)
1022 for (i = 0; i < tab_nc (t); i++)
1025 t->dim (r, t->dim_aux);
1027 for (i = 0; i < tab_nr (t); i++)
1029 error (0, 0, "height of table row %d is %d (not initialized?)",
1031 for (i = 0; i < tab_nc (t); i++)
1033 error (0, 0, "width of table column %d is %d (not initialized?)",
1036 /* Add up header sizes. */
1037 for (i = 0, r->wl = r->wrv[0]; i < r->l; i++)
1038 r->wl += r->w[i] + r->wrv[i + 1];
1039 for (i = 0, r->ht = r->hrh[0]; i < r->t; i++)
1040 r->ht += r->h[i] + r->hrh[i + 1];
1041 for (i = tab_nc (t) - r->r, r->wr = r->wrv[i]; i < tab_nc (t); i++)
1042 r->wr += r->w[i] + r->wrv[i + 1];
1043 for (i = tab_nr (t) - r->b, r->hb = r->hrh[i]; i < tab_nr (t); i++)
1044 r->hb += r->h[i] + r->hrh[i + 1];
1047 if (!(t->flags & SOMF_NO_TITLE))
1048 r->ht += driver->font_height;
1054 tabi_render_free (void *r_)
1056 struct tab_rendering *r = r_;
1065 /* Return the horizontal and vertical size of the entire table,
1066 including headers, for the current output device, into HORIZ and
1069 tabi_area (void *r_, int *horiz, int *vert)
1071 struct tab_rendering *r = r_;
1072 const struct tab_table *t = r->table;
1077 for (col = r->l + 1, width = r->wl + r->wr + r->w[tab_l (t)];
1078 col < tab_nc (t) - r->r; col++)
1079 width += r->w[col] + r->wrv[col];
1083 for (row = r->t + 1, height = r->ht + r->hb + r->h[tab_t (t)];
1084 row < tab_nr (t) - tab_b (t); row++)
1085 height += r->h[row] + r->hrh[row];
1089 /* Determines the number of rows or columns (including appropriate
1090 headers), depending on CUMTYPE, that will fit into the space
1091 specified. Takes rows/columns starting at index START and attempts
1092 to fill up available space MAX. Returns in END the index of the
1093 last row/column plus one; returns in ACTUAL the actual amount of
1094 space the selected rows/columns (including appropriate headers)
1097 tabi_cumulate (void *r_, int cumtype, int start, int *end,
1098 int max, int *actual)
1100 const struct tab_rendering *r = r_;
1101 const struct tab_table *t = r->table;
1107 assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1108 if (cumtype == SOM_ROWS)
1110 assert (start >= 0 && start < tab_nr (t));
1111 limit = tab_nr (t) - r->b;
1112 cells = &r->h[start];
1113 rules = &r->hrh[start + 1];
1114 total = r->ht + r->hb;
1118 assert (start >= 0 && start < tab_nc (t));
1119 limit = tab_nc (t) - tab_r (t);
1120 cells = &r->w[start];
1121 rules = &r->wrv[start + 1];
1122 total = r->wl + r->wr;
1135 for (idx = start + 1; idx < limit; idx++)
1137 int amt = *cells++ + *rules++;
1154 /* Render title for current table, with major index X and minor index
1155 Y. Y may be zero, or X and Y may be zero, but X should be nonzero
1158 tabi_title (void *r_, int x, int y, int table_num, int subtable_num,
1159 const char *command_name)
1161 const struct tab_rendering *r = r_;
1162 const struct tab_table *t = r->table;
1163 struct outp_text text;
1164 struct string title;
1166 if (t->flags & SOMF_NO_TITLE)
1169 ds_init_empty (&title);
1170 ds_put_format (&title,"%d.%d", table_num, subtable_num);
1172 ds_put_format (&title, "(%d:%d)", x, y);
1174 ds_put_format (&title, "(%d)", x);
1175 if (command_name != NULL)
1176 ds_put_format (&title, " %s", command_name);
1177 ds_put_cstr (&title, ". ");
1178 if (t->title != NULL)
1179 ds_put_cstr (&title, t->title);
1181 text.font = OUTP_PROPORTIONAL;
1182 text.justification = OUTP_LEFT;
1183 text.string = ds_ss (&title);
1184 text.h = r->driver->width;
1185 text.v = r->driver->font_height;
1187 text.y = r->driver->cp_y;
1188 r->driver->class->text_draw (r->driver, &text);
1190 ds_destroy (&title);
1193 static int render_strip (const struct tab_rendering *,
1194 int x, int y, int r, int c1, int c2, int r1, int r2);
1197 add_range (int ranges[][2], int *np, int start, int end)
1200 if (n == 0 || start > ranges[n - 1][1])
1202 ranges[n][0] = start;
1207 ranges[n - 1][1] = end;
1210 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1211 current position on the current output device. */
1213 tabi_render (void *r_, int c0, int r0, int c1, int r1)
1215 const struct tab_rendering *r = r_;
1216 const struct tab_table *t = r->table;
1217 int rows[3][2], cols[3][2];
1218 int n_row_ranges, n_col_ranges;
1221 /* Rows to render, counting horizontal rules as rows. */
1223 add_range (rows, &n_row_ranges, 0, tab_t (t) * 2 + 1);
1224 add_range (rows, &n_row_ranges, r0 * 2 + 1, r1 * 2);
1225 add_range (rows, &n_row_ranges, (tab_nr (t) - tab_b (t)) * 2,
1226 tab_nr (t) * 2 + 1);
1228 /* Columns to render, counting vertical rules as columns. */
1230 add_range (cols, &n_col_ranges, 0, r->l * 2 + 1);
1231 add_range (cols, &n_col_ranges, c0 * 2 + 1, c1 * 2);
1232 add_range (cols, &n_col_ranges, (tab_nc (t) - r->r) * 2, tab_nc (t) * 2 + 1);
1234 y = r->driver->cp_y;
1235 if (!(t->flags & SOMF_NO_TITLE))
1236 y += r->driver->font_height;
1237 for (i = 0; i < n_row_ranges; i++)
1241 for (row = rows[i][0]; row < rows[i][1]; row++)
1245 x = r->driver->cp_x;
1246 for (j = 0; j < n_col_ranges; j++)
1247 x = render_strip (r, x, y, row,
1248 cols[j][0], cols[j][1],
1249 rows[i][0], rows[i][1]);
1251 y += (row & 1) ? r->h[row / 2] : r->hrh[row / 2];
1256 const struct som_table_class tab_table_class =
1272 static enum outp_justification
1273 translate_justification (unsigned int opt)
1275 switch (opt & TAB_ALIGN_MASK)
1288 /* Returns the line style to use for drawing a rule of the given
1290 static enum outp_line_style
1291 rule_to_draw_type (unsigned char type)
1299 return OUTP_L_SINGLE;
1301 return OUTP_L_DOUBLE;
1307 /* Returns the horizontal rule at the given column and row. */
1309 get_hrule (const struct tab_table *t, int col, int row)
1311 return t->rh[col + row * t->cf];
1314 /* Returns the vertical rule at the given column and row. */
1316 get_vrule (const struct tab_table *t, int col, int row)
1318 return t->rv[col + row * (t->cf + 1)];
1321 /* Renders the horizontal rule at the given column and row
1322 at (X,Y) on the page. */
1324 render_horz_rule (const struct tab_rendering *r,
1325 int x, int y, int col, int row)
1327 enum outp_line_style style;
1328 style = rule_to_draw_type (get_hrule (r->table, col, row));
1329 if (style != OUTP_L_NONE)
1330 r->driver->class->line (r->driver, x, y, x + r->w[col], y + r->hrh[row],
1331 OUTP_L_NONE, style, OUTP_L_NONE, style);
1334 /* Renders the vertical rule at the given column and row
1335 at (X,Y) on the page. */
1337 render_vert_rule (const struct tab_rendering *r,
1338 int x, int y, int col, int row)
1340 enum outp_line_style style;
1341 style = rule_to_draw_type (get_vrule (r->table, col, row));
1342 if (style != OUTP_L_NONE)
1343 r->driver->class->line (r->driver, x, y, x + r->wrv[col], y + r->h[row],
1344 style, OUTP_L_NONE, style, OUTP_L_NONE);
1347 /* Renders the rule intersection at the given column and row
1348 at (X,Y) on the page. */
1350 render_rule_intersection (const struct tab_rendering *r,
1351 int x, int y, int col, int row)
1353 const struct tab_table *t = r->table;
1355 /* Bounds of intersection. */
1358 int x1 = x + r->wrv[col];
1359 int y1 = y + r->hrh[row];
1361 /* Lines on each side of intersection. */
1362 int top = row > 0 ? get_vrule (t, col, row - 1) : TAL_0;
1363 int left = col > 0 ? get_hrule (t, col - 1, row) : TAL_0;
1364 int bottom = row < tab_nr (t) ? get_vrule (t, col, row) : TAL_0;
1365 int right = col < tab_nc (t) ? get_hrule (t, col, row) : TAL_0;
1367 /* Output style for each line. */
1368 enum outp_line_style o_top = rule_to_draw_type (top);
1369 enum outp_line_style o_left = rule_to_draw_type (left);
1370 enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1371 enum outp_line_style o_right = rule_to_draw_type (right);
1373 if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1374 || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1375 r->driver->class->line (r->driver, x0, y0, x1, y1,
1376 o_top, o_left, o_bottom, o_right);
1379 /* Returns the width of columns C1...C2 exclusive,
1380 including interior but not exterior rules. */
1382 strip_width (const struct tab_rendering *r, int c1, int c2)
1387 for (c = c1; c < c2; c++)
1388 width += r->w[c] + r->wrv[c + 1];
1390 width -= r->wrv[c2];
1394 /* Returns the height of rows R1...R2 exclusive,
1395 including interior but not exterior rules. */
1397 strip_height (const struct tab_rendering *r, int r1, int r2)
1402 for (row = r1; row < r2; row++)
1403 height += r->h[row] + r->hrh[row + 1];
1405 height -= r->hrh[r2];
1409 /* Renders the cell at the given column and row at (X,Y) on the
1410 page. Also renders joined cells that extend as far to the
1411 right as C1 and as far down as R1. */
1413 render_cell (const struct tab_rendering *r,
1414 int x, int y, int col, int row, int c1, int r1)
1416 const struct tab_table *t = r->table;
1417 const int index = col + (row * t->cf);
1418 unsigned char type = t->ct[index];
1419 struct substring *content = &t->cc[index];
1421 if (!(type & TAB_JOIN))
1423 if (!(type & TAB_EMPTY))
1425 struct outp_text text;
1426 text.font = options_to_font (type);
1427 text.justification = translate_justification (type);
1428 text.string = *content;
1433 r->driver->class->text_draw (r->driver, &text);
1438 struct tab_joined_cell *j
1439 = (struct tab_joined_cell *) ss_data (*content);
1441 if (j->x1 == col && j->y1 == row)
1443 struct outp_text text;
1444 text.font = options_to_font (type);
1445 text.justification = translate_justification (type);
1446 text.string = j->contents;
1449 text.h = strip_width (r, j->x1, MIN (j->x2, c1));
1450 text.v = strip_height (r, j->y1, MIN (j->y2, r1));
1451 r->driver->class->text_draw (r->driver, &text);
1456 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1457 on row ROW, at (X,Y). Returns X position after rendering.
1458 Also renders joined cells that extend beyond that strip,
1459 cropping them to lie within rendering region (C0,R0)-(C1,R1).
1460 C0 and C1 count vertical rules as columns.
1461 ROW counts horizontal rules as rows, but R0 and R1 do not. */
1463 render_strip (const struct tab_rendering *r,
1464 int x, int y, int row, int c0, int c1, int r0 UNUSED, int r1)
1468 for (col = c0; col < c1; col++)
1472 render_cell (r, x, y, col / 2, row / 2, c1 / 2, r1);
1474 render_horz_rule (r, x, y, col / 2, row / 2);
1480 render_vert_rule (r, x, y, col / 2, row / 2);
1482 render_rule_intersection (r, x, y, col / 2, row / 2);
1483 x += r->wrv[col / 2];