1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 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, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
31 #include <data/data-out.h>
32 #include <data/format.h>
33 #include <data/value.h>
34 #include <libpspp/alloc.h>
35 #include <libpspp/assertion.h>
36 #include <libpspp/compiler.h>
37 #include <libpspp/magic.h>
38 #include <libpspp/misc.h>
39 #include <libpspp/pool.h>
44 #define _(msgid) gettext (msgid)
46 const struct som_table_class tab_table_class;
47 static char *command_name;
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, int reallocable UNUSED)
64 t = pool_create_container (struct tab_table, container);
65 t->col_style = TAB_COL_NONE;
71 t->l = t->r = t->t = t->b = 0;
73 t->cc = pool_nmalloc (t->container, nr * nc, sizeof *t->cc);
74 t->ct = pool_malloc (t->container, nr * nc);
75 memset (t->ct, TAB_EMPTY, nc * nr);
77 t->rh = pool_nmalloc (t->container, nc, nr + 1);
78 memset (t->rh, 0, nc * (nr + 1));
80 t->rv = pool_nmalloc (t->container, nr, nc + 1);
81 memset (t->rv, UCHAR_MAX, nr * (nc + 1));
85 t->col_ofs = t->row_ofs = 0;
90 /* Destroys table T. */
92 tab_destroy (struct tab_table *t)
96 pool_destroy (t->container);
99 /* Sets the width and height of a table, in columns and rows,
100 respectively. Use only to reduce the size of a table, since it
101 does not change the amount of allocated memory. */
103 tab_resize (struct tab_table *t, int nc, int nr)
108 assert (nc + t->col_ofs <= t->cf);
109 t->nc = nc + t->col_ofs;
113 assert (nr + t->row_ofs <= t->nr);
114 t->nr = nr + t->row_ofs;
118 /* Changes either or both dimensions of a table. Consider using the
119 above routine instead if it won't waste a lot of space.
121 Changing the number of columns in a table is particularly expensive
122 in space and time. Avoid doing such. FIXME: In fact, transferring
123 of rules isn't even implemented yet. */
125 tab_realloc (struct tab_table *t, int nc, int nr)
133 tab_offset (t, 0, 0);
140 assert (nc == t->nc);
144 int mr1 = MIN (nr, t->nr);
145 int mc1 = MIN (nc, t->nc);
147 struct substring *new_cc;
148 unsigned char *new_ct;
151 new_cc = pool_nmalloc (t->container, nr * nc, sizeof *new_cc);
152 new_ct = pool_malloc (t->container, nr * nc);
153 for (r = 0; r < mr1; r++)
155 memcpy (&new_cc[r * nc], &t->cc[r * t->nc], mc1 * sizeof *t->cc);
156 memcpy (&new_ct[r * nc], &t->ct[r * t->nc], mc1);
157 memset (&new_ct[r * nc + t->nc], TAB_EMPTY, nc - t->nc);
159 pool_free (t->container, t->cc);
160 pool_free (t->container, t->ct);
165 else if (nr != t->nr)
167 t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc);
168 t->ct = pool_realloc (t->container, t->ct, nr * nc);
170 t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
171 t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
175 memset (&t->rh[nc * (t->nr + 1)], TAL_0, (nr - t->nr) * nc);
176 memset (&t->rv[(nc + 1) * t->nr], UCHAR_MAX,
177 (nr - t->nr) * (nc + 1));
181 memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr));
187 tab_offset (t, co, ro);
190 /* Sets the number of header rows on each side of TABLE to L on the
191 left, R on the right, T on the top, B on the bottom. Header rows
192 are repeated when a table is broken across multiple columns or
195 tab_headers (struct tab_table *table, int l, int r, int t, int b)
197 assert (table != NULL);
198 assert (l < table->nc);
199 assert (r < table->nc);
200 assert (t < table->nr);
201 assert (b < table->nr);
210 /* Set up table T so that, when it is an appropriate size, it will be
211 displayed across the page in columns.
213 STYLE is a TAB_COL_* constant. GROUP is the number of rows to take
216 tab_columns (struct tab_table *t, int style, int group)
219 t->col_style = style;
220 t->col_group = group;
225 /* Draws a vertical line to the left of cells at horizontal position X
226 from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
228 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
233 if (x + t->col_ofs < 0 || x + t->col_ofs > t->nc
234 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
235 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
237 printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
238 "table size (%d,%d)\n"),
239 x, t->col_ofs, x + t->col_ofs,
240 y1, t->row_ofs, y1 + t->row_ofs,
241 y2, t->row_ofs, y2 + t->row_ofs,
255 assert (y2 <= t->nr);
260 for (y = y1; y <= y2; y++)
261 t->rv[x + (t->cf + 1) * y] = style;
265 /* Draws a horizontal line above cells at vertical position Y from X1
266 to X2 inclusive in style STYLE, if style is not -1. */
268 tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
285 for (x = x1; x <= x2; x++)
286 t->rh[x + t->cf * y] = style;
290 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
291 lines of style F_H and vertical lines of style F_V. Fills the
292 interior of the box with horizontal lines of style I_H and vertical
293 lines of style I_V. Any of the line styles may be -1 to avoid
294 drawing those lines. This is distinct from 0, which draws a null
297 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
298 int x1, int y1, int x2, int y2)
303 if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc
304 || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= t->nc
305 || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
306 || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
308 printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
309 "in table size (%d,%d)\n"),
310 x1, t->col_ofs, x1 + t->col_ofs,
311 y1, t->row_ofs, y1 + t->row_ofs,
312 x2, t->col_ofs, x2 + t->col_ofs,
313 y2, t->row_ofs, y2 + t->row_ofs,
334 for (x = x1; x <= x2; x++)
336 t->rh[x + t->cf * y1] = f_h;
337 t->rh[x + t->cf * (y2 + 1)] = f_h;
343 for (y = y1; y <= y2; y++)
345 t->rv[x1 + (t->cf + 1) * y] = f_v;
346 t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
354 for (y = y1 + 1; y <= y2; y++)
358 for (x = x1; x <= x2; x++)
359 t->rh[x + t->cf * y] = i_h;
366 for (x = x1 + 1; x <= x2; x++)
370 for (y = y1; y <= y2; y++)
371 t->rv[x + (t->cf + 1) * y] = i_v;
376 /* Formats text TEXT and arguments ARGS as indicated in OPT in
377 TABLE's pool and returns the resultant string. */
378 static struct substring
379 text_format (struct tab_table *table, int opt, const char *text, va_list args)
381 assert (table != NULL && text != NULL);
383 return ss_cstr (opt & TAT_PRINTF
384 ? pool_vasprintf (table->container, text, args)
385 : pool_strdup (table->container, text));
388 /* Set the title of table T to TITLE, which is formatted as if
389 passed to printf(). */
391 tab_title (struct tab_table *t, const char *title, ...)
395 assert (t != NULL && title != NULL);
396 va_start (args, title);
397 t->title = xvasprintf (title, args);
401 /* Set DIM_FUNC as the dimension function for table T. */
403 tab_dim (struct tab_table *t, tab_dim_func *dim_func)
405 assert (t != NULL && t->dim == NULL);
409 /* Returns the natural width of column C in table T for driver D, that
410 is, the smallest width necessary to display all its cells without
411 wrapping. The width will be no larger than the page width minus
412 left and right rule widths. */
414 tab_natural_width (struct tab_table *t, struct outp_driver *d, int c)
418 assert (t != NULL && c >= 0 && c < t->nc);
422 for (width = r = 0; r < t->nr; r++)
424 struct outp_text text;
425 unsigned char opt = t->ct[c + r * t->cf];
428 if (opt & (TAB_JOIN | TAB_EMPTY))
431 text.string = t->cc[c + r * t->cf];
432 text.justification = OUTP_LEFT;
433 text.font = options_to_font (opt);
434 text.h = text.v = INT_MAX;
436 d->class->text_metrics (d, &text, &w, NULL);
444 /* FIXME: This is an ugly kluge to compensate for the fact
445 that we don't let joined cells contribute to column
447 width = d->prop_em_width * 8;
451 const int clamp = d->width - t->wrv[0] - t->wrv[t->nc];
460 /* Returns the natural height of row R in table T for driver D, that
461 is, the minimum height necessary to display the information in the
462 cell at the widths set for each column. */
464 tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
468 assert (t != NULL && r >= 0 && r < t->nr);
473 for (height = d->font_height, c = 0; c < t->nc; c++)
475 struct outp_text text;
476 unsigned char opt = t->ct[c + r * t->cf];
479 assert (t->w[c] != NOT_INT);
480 if (opt & (TAB_JOIN | TAB_EMPTY))
483 text.string = t->cc[c + r * t->cf];
484 text.justification = OUTP_LEFT;
485 text.font = options_to_font (opt);
488 d->class->text_metrics (d, &text, NULL, &h);
498 /* Callback function to set all columns and rows to their natural
499 dimensions. Not really meant to be called directly. */
501 tab_natural_dimensions (struct tab_table *t, struct outp_driver *d)
507 for (i = 0; i < t->nc; i++)
508 t->w[i] = tab_natural_width (t, d, i);
510 for (i = 0; i < t->nr; i++)
511 t->h[i] = tab_natural_height (t, d, i);
517 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
518 from V, displayed with format spec F. */
520 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
521 const union value *v, const struct fmt_spec *f)
525 assert (table != NULL && v != NULL && f != NULL);
527 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
528 || c + table->col_ofs >= table->nc
529 || r + table->row_ofs >= table->nr)
531 printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
533 c, table->col_ofs, c + table->col_ofs,
534 r, table->row_ofs, r + table->row_ofs,
535 table->nc, table->nr);
540 contents = pool_alloc (table->container, f->w);
541 table->cc[c + r * table->cf] = ss_buffer (contents, f->w);
542 table->ct[c + r * table->cf] = opt;
544 data_out (v, f, contents);
547 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
548 with NDEC decimal places. */
550 tab_float (struct tab_table *table, int c, int r, unsigned char opt,
551 double val, int w, int d)
557 union value double_value;
559 assert (table != NULL && w <= 40);
562 assert (c < table->nc);
564 assert (r < table->nr);
566 f = fmt_for_output (FMT_F, w, d);
569 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
570 || c + table->col_ofs >= table->nc
571 || r + table->row_ofs >= table->nr)
573 printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
575 c, table->col_ofs, c + table->col_ofs,
576 r, table->row_ofs, r + table->row_ofs,
577 table->nc, table->nr);
582 double_value.f = val;
583 data_out (&double_value, &f, buf);
586 while (isspace ((unsigned char) *cp) && cp < &buf[w])
588 f.w = w - (cp - buf);
590 contents = pool_alloc (table->container, f.w);
591 table->cc[c + r * table->cf] = ss_buffer (contents, f.w);
592 table->ct[c + r * table->cf] = opt;
593 memcpy (contents, cp, f.w);
596 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
599 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
603 assert (table != NULL && text != NULL);
607 assert (c < table->nc);
608 assert (r < table->nr);
612 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
613 || c + table->col_ofs >= table->nc
614 || r + table->row_ofs >= table->nr)
616 printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
618 c, table->col_ofs, c + table->col_ofs,
619 r, table->row_ofs, r + table->row_ofs,
620 table->nc, table->nr);
625 va_start (args, text);
626 table->cc[c + r * table->cf] = text_format (table, opt, text, args);
627 table->ct[c + r * table->cf] = opt;
631 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
632 options OPT to have text value TEXT. */
634 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
635 unsigned opt, const char *text, ...)
637 struct tab_joined_cell *j;
639 assert (table != NULL && text != NULL);
641 assert (x1 + table->col_ofs >= 0);
642 assert (y1 + table->row_ofs >= 0);
645 assert (y2 + table->row_ofs < table->nr);
646 assert (x2 + table->col_ofs < table->nc);
649 if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
650 || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
651 || x2 < x1 || x2 + table->col_ofs >= table->nc
652 || y2 < y2 || y2 + table->row_ofs >= table->nr)
654 printf ("tab_joint_text(): bad cell "
655 "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
656 x1, table->col_ofs, x1 + table->col_ofs,
657 y1, table->row_ofs, y1 + table->row_ofs,
658 x2, table->col_ofs, x2 + table->col_ofs,
659 y2, table->row_ofs, y2 + table->row_ofs,
660 table->nc, table->nr);
665 tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
667 j = pool_alloc (table->container, sizeof *j);
669 j->x1 = x1 + table->col_ofs;
670 j->y1 = y1 + table->row_ofs;
671 j->x2 = ++x2 + table->col_ofs;
672 j->y2 = ++y2 + table->row_ofs;
677 va_start (args, text);
678 j->contents = text_format (table, opt, text, args);
685 struct substring *cc = &table->cc[x1 + y1 * table->cf];
686 unsigned char *ct = &table->ct[x1 + y1 * table->cf];
687 const int ofs = table->cf - (x2 - x1);
691 for (y = y1; y < y2; y++)
695 for (x = x1; x < x2; x++)
697 *cc++ = ss_buffer ((char *) j, 0);
707 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
709 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
710 struct substring *string)
712 assert (table != NULL && string != NULL);
715 if (c + table->col_ofs < 0 || r + table->row_ofs < 0
716 || c + table->col_ofs >= table->nc
717 || r + table->row_ofs >= table->nr)
719 printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
721 c, table->col_ofs, c + table->col_ofs,
722 r, table->row_ofs, r + table->row_ofs,
723 table->nc, table->nr);
728 table->cc[c + r * table->cf] = *string;
729 table->ct[c + r * table->cf] = opt;
734 /* Sets the widths of all the columns and heights of all the rows in
735 table T for driver D. */
737 nowrap_dim (struct tab_table *t, struct outp_driver *d)
739 t->w[0] = tab_natural_width (t, d, 0);
740 t->h[0] = d->font_height;
743 /* Sets the widths of all the columns and heights of all the rows in
744 table T for driver D. */
746 wrap_dim (struct tab_table *t, struct outp_driver *d)
748 t->w[0] = tab_natural_width (t, d, 0);
749 t->h[0] = tab_natural_height (t, d, 0);
752 /* Outputs text BUF as a table with a single cell having cell options
753 OPTIONS, which is a combination of the TAB_* and TAT_*
756 tab_output_text (int options, const char *buf, ...)
758 struct tab_table *t = tab_create (1, 1, 0);
759 char *tmp_buf = NULL;
761 if (options & TAT_PRINTF)
765 va_start (args, buf);
766 buf = tmp_buf = xvasprintf (buf, args);
770 tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
771 tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
772 tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim);
778 /* Set table flags to FLAGS. */
780 tab_flags (struct tab_table *t, unsigned flags)
786 /* Easy, type-safe way to submit a tab table to som. */
788 tab_submit (struct tab_table *t)
793 s.class = &tab_table_class;
802 /* Set table row and column offsets for all functions that affect
805 tab_offset (struct tab_table *t, int col, int row)
811 if (row < -1 || row > t->nr)
813 printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
816 if (col < -1 || col > t->nc)
818 printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
824 diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
826 diff += (col - t->col_ofs), t->col_ofs = col;
832 /* Increment the row offset by one. If the table is too small,
833 increase its size. */
835 tab_next_row (struct tab_table *t)
840 if (++t->row_ofs >= t->nr)
841 tab_realloc (t, -1, t->nr * 4 / 3);
844 static struct tab_table *t;
845 static struct outp_driver *d;
848 /* Set the current table to TABLE. */
850 tabi_table (struct som_entity *table)
852 assert (table != NULL);
853 assert (table->type == SOM_TABLE);
856 tab_offset (t, 0, 0);
858 assert (t->w == NULL && t->h == NULL);
859 t->w = pool_nalloc (t->container, t->nc, sizeof *t->w);
860 t->h = pool_nalloc (t->container, t->nr, sizeof *t->h);
861 t->hrh = pool_nmalloc (t->container, t->nr + 1, sizeof *t->hrh);
862 t->wrv = pool_nmalloc (t->container, t->nc + 1, sizeof *t->wrv);
865 /* Returns the line style to use for spacing purposes for a rule
866 of the given TYPE. */
867 static enum outp_line_style
868 rule_to_spacing_type (unsigned char type)
876 return OUTP_L_SINGLE;
878 return OUTP_L_DOUBLE;
884 /* Set the current output device to DRIVER. */
886 tabi_driver (struct outp_driver *driver)
891 assert (driver != NULL);
894 /* Figure out sizes of rules. */
895 for (r = 0; r <= t->nr; r++)
898 for (c = 0; c < t->nc; c++)
900 unsigned char rh = t->rh[c + r * t->cf];
901 int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
908 for (c = 0; c <= t->nc; c++)
911 for (r = 0; r < t->nr; r++)
913 unsigned char *rv = &t->rv[c + r * (t->cf + 1)];
915 if (*rv == UCHAR_MAX)
916 *rv = c != 0 && c != t->nc ? TAL_GAP : TAL_0;
917 w = driver->vert_line_width[rule_to_spacing_type (*rv)];
925 for (i = 0; i < t->nr; i++)
927 for (i = 0; i < t->nc; i++)
931 assert (t->dim != NULL);
938 for (i = 0; i < t->nr; i++)
942 printf ("Table row %d height not initialized.\n", i);
945 assert (t->h[i] > 0);
948 for (i = 0; i < t->nc; i++)
952 printf ("Table column %d width not initialized.\n", i);
955 assert (t->w[i] > 0);
960 /* Add up header sizes. */
961 for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
962 t->wl += t->w[i] + t->wrv[i + 1];
963 for (i = 0, t->ht = t->hrh[0]; i < t->t; i++)
964 t->ht += t->h[i] + t->hrh[i + 1];
965 for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++)
966 t->wr += t->w[i] + t->wrv[i + 1];
967 for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
968 t->hb += t->h[i] + t->hrh[i + 1];
971 if (!(t->flags & SOMF_NO_TITLE))
972 t->ht += d->font_height;
975 /* Return the number of columns and rows in the table into N_COLUMNS
976 and N_ROWS, respectively. */
978 tabi_count (int *n_columns, int *n_rows)
980 assert (n_columns != NULL && n_rows != NULL);
985 static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual);
987 /* Return the horizontal and vertical size of the entire table,
988 including headers, for the current output device, into HORIZ and
991 tabi_area (int *horiz, int *vert)
993 assert (horiz != NULL && vert != NULL);
998 for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
999 c < t->nc - t->r; c++)
1000 w += t->w[c] + t->wrv[c];
1006 for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
1007 r < t->nr - t->b; r++)
1008 h += t->h[r] + t->hrh[r];
1013 /* Return the column style for this table into STYLE. */
1015 tabi_columns (int *style)
1017 assert (style != NULL);
1018 *style = t->col_style;
1021 /* Return the number of header rows/columns on the left, right, top,
1022 and bottom sides into HL, HR, HT, and HB, respectively. */
1024 tabi_headers (int *hl, int *hr, int *ht, int *hb)
1026 assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL);
1033 /* Determines the number of rows or columns (including appropriate
1034 headers), depending on CUMTYPE, that will fit into the space
1035 specified. Takes rows/columns starting at index START and attempts
1036 to fill up available space MAX. Returns in END the index of the
1037 last row/column plus one; returns in ACTUAL the actual amount of
1038 space the selected rows/columns (including appropriate headers)
1041 tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
1048 assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1049 if (cumtype == SOM_ROWS)
1051 assert (start >= 0 && start < t->nr);
1054 r = &t->hrh[start + 1];
1055 total = t->ht + t->hb;
1059 assert (start >= 0 && start < t->nc);
1062 r = &t->wrv[start + 1];
1063 total = t->wl + t->wr;
1079 for (x = start + 1; x < n; x++)
1081 int amt = *d++ + *r++;
1099 /* Return flags set for the current table into FLAGS. */
1101 tabi_flags (unsigned *flags)
1103 assert (flags != NULL);
1107 /* Returns true if the table will fit in the given page WIDTH,
1110 tabi_fits_width (int width)
1114 for (i = t->l; i < t->nc - t->r; i++)
1115 if (t->wl + t->wr + t->w[i] > width)
1121 /* Returns true if the table will fit in the given page LENGTH,
1124 tabi_fits_length (int length)
1128 for (i = t->t; i < t->nr - t->b; i++)
1129 if (t->ht + t->hb + t->h[i] > length)
1135 /* Sets the number of header rows/columns on the left, right, top,
1136 and bottom sides to HL, HR, HT, and HB, respectively. */
1138 tabi_set_headers (int hl, int hr, int ht, int hb)
1146 /* Render title for current table, with major index X and minor index
1147 Y. Y may be zero, or X and Y may be zero, but X should be nonzero
1150 tabi_title (int x, int y)
1155 if (t->flags & SOMF_NO_TITLE)
1158 cp = spprintf (buf, "%d.%d", table_num, subtable_num);
1160 cp = spprintf (cp, "(%d:%d)", x, y);
1162 cp = spprintf (cp, "(%d)", x);
1163 if (command_name != NULL)
1164 cp = spprintf (cp, " %s", command_name);
1165 cp = stpcpy (cp, ". ");
1166 if (t->title != NULL)
1168 size_t length = strlen (t->title);
1169 memcpy (cp, t->title, length);
1175 struct outp_text text;
1177 text.font = OUTP_PROPORTIONAL;
1178 text.justification = OUTP_LEFT;
1179 text.string = ss_buffer (buf, cp - buf);
1181 text.v = d->font_height;
1184 d->class->text_draw (d, &text);
1188 static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
1190 /* Renders columns C0...C1, plus headers, of rows R0...R1,
1191 at the given vertical position Y.
1192 C0 and C1 count vertical rules as columns,
1193 but R0 and R1 do not count horizontal rules as rows.
1194 Returns the vertical position after rendering. */
1196 render_rows (int y, int c0, int c1, int r0, int r1)
1199 for (r = r0; r < r1; r++)
1202 x = render_strip (x, y, r, 0, t->l * 2 + 1, r0, r1);
1203 x = render_strip (x, y, r, c0 * 2 + 1, c1 * 2, r0, r1);
1204 x = render_strip (x, y, r, (t->nc - t->r) * 2, t->nc * 2 + 1, r0, r1);
1205 y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2];
1210 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1211 current position on the current output device. */
1213 tabi_render (int c0, int r0, int c1, int r1)
1220 if (!(t->flags & SOMF_NO_TITLE))
1221 y += d->font_height;
1223 y = render_rows (y, c0, c1, 0, t->t * 2 + 1);
1224 y = render_rows (y, c0, c1, r0 * 2 + 1, r1 * 2);
1225 y = render_rows (y, c0, c1, (t->nr - t->b) * 2, t->nr * 2 + 1);
1228 const struct som_table_class tab_table_class =
1254 static enum outp_justification
1255 translate_justification (unsigned int opt)
1257 switch (opt & TAB_ALIGN_MASK)
1270 /* Returns the line style to use for drawing a rule of the given
1272 static enum outp_line_style
1273 rule_to_draw_type (unsigned char type)
1281 return OUTP_L_SINGLE;
1283 return OUTP_L_DOUBLE;
1289 /* Returns the horizontal rule at the given column and row. */
1291 get_hrule (int c, int r)
1293 return t->rh[c + r * t->cf];
1296 /* Returns the vertical rule at the given column and row. */
1298 get_vrule (int c, int r)
1300 return t->rv[c + r * (t->cf + 1)];
1303 /* Renders the horizontal rule at the given column and row
1304 at (X,Y) on the page. */
1306 render_horz_rule (int x, int y, int c, int r)
1308 enum outp_line_style style = rule_to_draw_type (get_hrule (c, r));
1309 if (style != OUTP_L_NONE)
1310 d->class->line (d, x, y, x + t->w[c], y + t->hrh[r],
1311 OUTP_L_NONE, style, OUTP_L_NONE, style);
1314 /* Renders the vertical rule at the given column and row
1315 at (X,Y) on the page. */
1317 render_vert_rule (int x, int y, int c, int r)
1319 enum outp_line_style style = rule_to_draw_type (get_vrule (c, r));
1320 if (style != OUTP_L_NONE)
1321 d->class->line (d, x, y, x + t->wrv[c], y + t->h[r],
1322 style, OUTP_L_NONE, style, OUTP_L_NONE);
1325 /* Renders the rule intersection at the given column and row
1326 at (X,Y) on the page. */
1328 render_rule_intersection (int x, int y, int c, int r)
1330 /* Bounds of intersection. */
1333 int x1 = x + t->wrv[c];
1334 int y1 = y + t->hrh[r];
1336 /* Lines on each side of intersection. */
1337 int top = r > 0 ? get_vrule (c, r - 1) : TAL_0;
1338 int left = c > 0 ? get_hrule (c - 1, r) : TAL_0;
1339 int bottom = r < t->nr ? get_vrule (c, r) : TAL_0;
1340 int right = c < t->nc ? get_hrule (c, r) : TAL_0;
1342 /* Output style for each line. */
1343 enum outp_line_style o_top = rule_to_draw_type (top);
1344 enum outp_line_style o_left = rule_to_draw_type (left);
1345 enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1346 enum outp_line_style o_right = rule_to_draw_type (right);
1348 if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1349 || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1350 d->class->line (d, x0, y0, x1, y1, o_top, o_left, o_bottom, o_right);
1353 /* Returns the width of columns C1...C2 exclusive,
1354 including interior but not exterior rules. */
1356 strip_width (int c1, int c2)
1361 for (c = c1; c < c2; c++)
1362 width += t->w[c] + t->wrv[c + 1];
1364 width -= t->wrv[c2];
1368 /* Returns the height of rows R1...R2 exclusive,
1369 including interior but not exterior rules. */
1371 strip_height (int r1, int r2)
1376 for (r = r1; r < r2; r++)
1377 height += t->h[r] + t->hrh[r + 1];
1379 height -= t->hrh[r2];
1383 /* Renders the cell at the given column and row at (X,Y) on the
1384 page. Also renders joined cells that extend as far to the
1385 right as C1 and as far down as R1. */
1387 render_cell (int x, int y, int c, int r, int c1, int r1)
1389 const int index = c + (r * t->cf);
1390 unsigned char type = t->ct[index];
1391 struct substring *content = &t->cc[index];
1393 if (!(type & TAB_JOIN))
1395 if (!(type & TAB_EMPTY))
1397 struct outp_text text;
1398 text.font = options_to_font (type);
1399 text.justification = translate_justification (type);
1400 text.string = *content;
1405 d->class->text_draw (d, &text);
1410 struct tab_joined_cell *j
1411 = (struct tab_joined_cell *) ss_data (*content);
1413 if (j->hit != tab_hit)
1417 if (j->x1 == c && j->y1 == r)
1419 struct outp_text text;
1420 text.font = options_to_font (type);
1421 text.justification = translate_justification (type);
1422 text.string = j->contents;
1425 text.h = strip_width (j->x1, MIN (j->x2, c1));
1426 text.v = strip_height (j->y1, MIN (j->y2, r1));
1427 d->class->text_draw (d, &text);
1433 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1434 on row R, at (X,Y). Returns X position after rendering.
1435 Also renders joined cells that extend beyond that strip,
1436 cropping them to lie within rendering region (C0,R0)-(C1,R1).
1437 C0 and C1 count vertical rules as columns.
1438 R counts horizontal rules as rows, but R0 and R1 do not. */
1440 render_strip (int x, int y, int r, int c0, int c1, int r0 UNUSED, int r1)
1444 for (c = c0; c < c1; c++)
1448 render_cell (x, y, c / 2, r / 2, c1 / 2, r1);
1450 render_horz_rule (x, y, c / 2, r / 2);
1456 render_vert_rule (x, y, c / 2, r / 2);
1458 render_rule_intersection (x, y, c / 2, r / 2);
1465 /* Sets COMMAND_NAME as the name of the current command,
1466 for embedding in output. */
1468 tab_set_command_name (const char *command_name_)
1470 free (command_name);
1471 command_name = command_name_ ? xstrdup (command_name_) : NULL;