Delete tab_create()'s unused third parameter.
[pspp-builds.git] / src / output / table.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2009 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include "table.h"
20
21 #include <ctype.h>
22 #include <stdarg.h>
23 #include <limits.h>
24 #include <stdlib.h>
25
26 #include "output.h"
27 #include "manager.h"
28
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>
36
37 #include <data/settings.h>
38
39 #include "error.h"
40 #include "minmax.h"
41 #include "xalloc.h"
42
43 #include "gettext.h"
44 #define _(msgid) gettext (msgid)
45 \f
46 const struct som_table_class tab_table_class;
47
48 /* Returns the font to use for a cell with the given OPTIONS. */
49 static enum outp_font
50 options_to_font (unsigned options)
51 {
52   return (options & TAB_FIX ? OUTP_FIXED
53           : options & TAB_EMPH ? OUTP_EMPHASIS
54           : OUTP_PROPORTIONAL);
55 }
56
57 /* Creates a table with NC columns and NR rows. */
58 struct tab_table *
59 tab_create (int nc, int nr)
60 {
61   struct tab_table *t;
62
63   t = pool_create_container (struct tab_table, container);
64   t->ref_cnt = 1;
65   t->col_style = TAB_COL_NONE;
66   t->title = NULL;
67   t->flags = SOMF_NONE;
68   t->nr = nr;
69   t->nc = t->cf = nc;
70   t->l = t->r = t->t = t->b = 0;
71
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);
75
76   t->rh = pool_nmalloc (t->container, nc, nr + 1);
77   memset (t->rh, 0, nc * (nr + 1));
78
79   t->rv = pool_nmalloc (t->container, nr, nc + 1);
80   memset (t->rv, UCHAR_MAX, nr * (nc + 1));
81
82   t->dim = NULL;
83   t->col_ofs = t->row_ofs = 0;
84
85   return t;
86 }
87
88 /* Increases T's reference count and, if this causes T's
89    reference count to reach 0, destroys T. */
90 void
91 tab_destroy (struct tab_table *t)
92 {
93   assert (t->ref_cnt > 0);
94   if (--t->ref_cnt > 0)
95     return;
96   if (t->dim_free != NULL)
97     t->dim_free (t->dim_aux);
98   free (t->title);
99   pool_destroy (t->container);
100 }
101
102 /* Increases T's reference count. */
103 void
104 tab_ref (struct tab_table *t)
105 {
106   assert (t->ref_cnt > 0);
107   t->ref_cnt++;
108 }
109
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. */
113 void
114 tab_resize (struct tab_table *t, int nc, int nr)
115 {
116   assert (t != NULL);
117   if (nc != -1)
118     {
119       assert (nc + t->col_ofs <= t->cf);
120       t->nc = nc + t->col_ofs;
121     }
122   if (nr != -1)
123     {
124       assert (nr + t->row_ofs <= tab_nr (t));
125       t->nr = nr + t->row_ofs;
126     }
127 }
128
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.
131
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. */
135 void
136 tab_realloc (struct tab_table *t, int nc, int nr)
137 {
138   int ro, co;
139
140   assert (t != NULL);
141   ro = t->row_ofs;
142   co = t->col_ofs;
143   if (ro || co)
144     tab_offset (t, 0, 0);
145
146   if (nc == -1)
147     nc = tab_nc (t);
148   if (nr == -1)
149     nr = tab_nr (t);
150
151   assert (nc == tab_nc (t));
152
153   if (nc > t->cf)
154     {
155       int mr1 = MIN (nr, tab_nr (t));
156       int mc1 = MIN (nc, tab_nc (t));
157
158       struct substring *new_cc;
159       unsigned char *new_ct;
160       int r;
161
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++)
165         {
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));
169         }
170       pool_free (t->container, t->cc);
171       pool_free (t->container, t->ct);
172       t->cc = new_cc;
173       t->ct = new_ct;
174       t->cf = nc;
175     }
176   else if (nr != tab_nr (t))
177     {
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);
180
181       t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
182       t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
183
184       if (nr > tab_nr (t))
185         {
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));
189         }
190     }
191
192   memset (&t->ct[nc * tab_nr (t)], TAB_EMPTY, nc * (nr - tab_nr (t)));
193
194   t->nr = nr;
195   t->nc = nc;
196
197   if (ro || co)
198     tab_offset (t, co, ro);
199 }
200
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
204    multiple pages. */
205 void
206 tab_headers (struct tab_table *table, int l, int r, int t, int b)
207 {
208   assert (table != NULL);
209   assert (l < table->nc);
210   assert (r < table->nc);
211   assert (t < table->nr);
212   assert (b < table->nr);
213
214
215   table->l = l;
216   table->r = r;
217   table->t = t;
218   table->b = b;
219 }
220
221 /* Set up table T so that, when it is an appropriate size, it will be
222    displayed across the page in columns.
223
224    STYLE is a TAB_COL_* constant. */
225 void
226 tab_columns (struct tab_table *t, int style)
227 {
228   assert (t != NULL);
229   t->col_style = style;
230 }
231 \f
232 /* Rules. */
233
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. */
236 void
237 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
238 {
239   assert (t != NULL);
240
241 #if DEBUGGING
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))
245     {
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));
252       return;
253     }
254 #endif
255
256   x += t->col_ofs;
257   y1 += t->row_ofs;
258   y2 += t->row_ofs;
259
260   assert (x  > 0);
261   assert (x  < tab_nc (t));
262   assert (y1 >= 0);
263   assert (y2 >= y1);
264   assert (y2 <=  tab_nr (t));
265
266   if (style != -1)
267     {
268       int y;
269       for (y = y1; y <= y2; y++)
270         t->rv[x + (t->cf + 1) * y] = style;
271     }
272 }
273
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. */
276 void
277 tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
278 {
279   assert (t != NULL);
280
281   x1 += t->col_ofs;
282   x2 += t->col_ofs;
283   y += t->row_ofs;
284
285   assert (y >= 0);
286   assert (y <= tab_nr (t));
287   assert (x2 >= x1 );
288   assert (x1 >= 0 );
289   assert (x2 < tab_nc (t));
290
291   if (style != -1)
292     {
293       int x;
294       for (x = x1; x <= x2; x++)
295         t->rh[x + t->cf * y] = style;
296     }
297 }
298
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
304    line. */
305 void
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)
308 {
309   assert (t != NULL);
310
311 #if DEBUGGING
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))
316     {
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));
324       NOT_REACHED ();
325     }
326 #endif
327
328   x1 += t->col_ofs;
329   x2 += t->col_ofs;
330   y1 += t->row_ofs;
331   y2 += t->row_ofs;
332
333   assert (x2 >= x1);
334   assert (y2 >= y1);
335   assert (x1 >= 0);
336   assert (y1 >= 0);
337   assert (x2 < tab_nc (t));
338   assert (y2 < tab_nr (t));
339
340   if (f_h != -1)
341     {
342       int x;
343       for (x = x1; x <= x2; x++)
344         {
345           t->rh[x + t->cf * y1] = f_h;
346           t->rh[x + t->cf * (y2 + 1)] = f_h;
347         }
348     }
349   if (f_v != -1)
350     {
351       int y;
352       for (y = y1; y <= y2; y++)
353         {
354           t->rv[x1 + (t->cf + 1) * y] = f_v;
355           t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
356         }
357     }
358
359   if (i_h != -1)
360     {
361       int y;
362
363       for (y = y1 + 1; y <= y2; y++)
364         {
365           int x;
366
367           for (x = x1; x <= x2; x++)
368             t->rh[x + t->cf * y] = i_h;
369         }
370     }
371   if (i_v != -1)
372     {
373       int x;
374
375       for (x = x1 + 1; x <= x2; x++)
376         {
377           int y;
378
379           for (y = y1; y <= y2; y++)
380             t->rv[x + (t->cf + 1) * y] = i_v;
381         }
382     }
383 }
384
385 /* Set the title of table T to TITLE, which is formatted as if
386    passed to printf(). */
387 void
388 tab_title (struct tab_table *t, const char *title, ...)
389 {
390   va_list args;
391
392   assert (t != NULL && title != NULL);
393   va_start (args, title);
394   t->title = xvasprintf (title, args);
395   va_end (args);
396 }
397
398 /* Set DIM_FUNC, which will be passed auxiliary data AUX, as the
399    dimension function for table T.
400
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.  */
407 void
408 tab_dim (struct tab_table *t,
409          tab_dim_func *dim_func, tab_dim_free_func *free_func, void *aux)
410 {
411   assert (t->dim == NULL);
412   t->dim = dim_func;
413   t->dim_free = free_func;
414   t->dim_aux = aux;
415 }
416
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. */
421 int
422 tab_natural_width (const struct tab_rendering *r, int col)
423 {
424   const struct tab_table *t = r->table;
425   int width, row, max_width;
426
427   assert (col >= 0 && col < tab_nc (t));
428
429   width = 0;
430   for (row = 0; row < tab_nr (t); row++)
431     {
432       struct outp_text text;
433       unsigned char opt = t->ct[col + row * t->cf];
434       int w;
435
436       if (opt & (TAB_JOIN | TAB_EMPTY))
437         continue;
438
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;
443
444       r->driver->class->text_metrics (r->driver, &text, &w, NULL);
445       if (w > width)
446         width = w;
447     }
448
449   if (width == 0)
450     {
451       /* FIXME: This is an ugly kluge to compensate for the fact
452          that we don't let joined cells contribute to column
453          widths. */
454       width = r->driver->prop_em_width * 8;
455     }
456
457   max_width = r->driver->width - r->wrv[0] - r->wrv[tab_nc (t)];
458   return MIN (width, max_width);
459 }
460
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. */
464 int
465 tab_natural_height (const struct tab_rendering *r, int row)
466 {
467   const struct tab_table *t = r->table;
468   int height, col;
469
470   assert (row >= 0 && row < tab_nr (t));
471
472   height = r->driver->font_height;
473   for (col = 0; col < tab_nc (t); col++)
474     {
475       struct outp_text text;
476       unsigned char opt = t->ct[col + row * t->cf];
477       int h;
478
479       if (opt & (TAB_JOIN | TAB_EMPTY))
480         continue;
481
482       text.string = t->cc[col + row * t->cf];
483       text.justification = OUTP_LEFT;
484       text.font = options_to_font (opt);
485       text.h = r->w[col];
486       text.v = INT_MAX;
487       r->driver->class->text_metrics (r->driver, &text, NULL, &h);
488
489       if (h > height)
490         height = h;
491     }
492
493   return height;
494 }
495
496 /* Callback function to set all columns and rows to their natural
497    dimensions.  Not really meant to be called directly.  */
498 void
499 tab_natural_dimensions (struct tab_rendering *r, void *aux UNUSED)
500 {
501   const struct tab_table *t = r->table;
502   int i;
503
504   for (i = 0; i < tab_nc (t); i++)
505     r->w[i] = tab_natural_width (r, i);
506
507   for (i = 0; i < tab_nr (t); i++)
508     r->h[i] = tab_natural_height (r, i);
509 }
510
511 \f
512 /* Cells. */
513
514 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
515    from V, displayed with format spec F. */
516 void
517 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
518            const union value *v, const struct fmt_spec *f)
519 {
520   char *contents;
521
522   assert (table != NULL && v != NULL && f != NULL);
523 #if DEBUGGING
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))
527     {
528       printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
529               "(%d,%d)\n",
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));
533       return;
534     }
535 #endif
536
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;
540
541   data_out (v, f, contents);
542 }
543
544 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
545    with NDEC decimal places. */
546 void
547 tab_fixed (struct tab_table *table, int c, int r, unsigned char opt,
548            double val, int w, int d)
549 {
550   char *contents;
551   char buf[40], *cp;
552
553   struct fmt_spec f;
554   union value double_value;
555
556   assert (table != NULL && w <= 40);
557
558   assert (c >= 0);
559   assert (c < tab_nc (table));
560   assert (r >= 0);
561   assert (r < tab_nr (table));
562
563   f = fmt_for_output (FMT_F, w, d);
564
565 #if DEBUGGING
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))
569     {
570       printf ("tab_fixed(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
571               "(%d,%d)\n",
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));
575       return;
576     }
577 #endif
578
579   double_value.f = val;
580   data_out (&double_value, &f, buf);
581
582   cp = buf;
583   while (isspace ((unsigned char) *cp) && cp < &buf[w])
584     cp++;
585   f.w = w - (cp - buf);
586
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);
591 }
592
593 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL as
594    formatted by FMT.
595    If FMT is null, then the default print format will be used.
596 */
597 void
598 tab_double (struct tab_table *table, int c, int r, unsigned char opt,
599            double val, const struct fmt_spec *fmt)
600 {
601   int w;
602   char *contents;
603   char buf[40], *cp;
604
605   union value double_value;
606
607   assert (table != NULL);
608
609   assert (c >= 0);
610   assert (c < tab_nc (table));
611   assert (r >= 0);
612   assert (r < tab_nr (table));
613
614   if ( fmt == NULL)
615     fmt = settings_get_format ();
616
617   fmt_check_output (fmt);
618
619 #if DEBUGGING
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))
623     {
624       printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
625               "(%d,%d)\n",
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));
629       return;
630     }
631 #endif
632
633   double_value.f = val;
634   data_out (&double_value, fmt, buf);
635
636   cp = buf;
637   while (isspace ((unsigned char) *cp) && cp < &buf[fmt->w])
638     cp++;
639   w = fmt->w - (cp - buf);
640
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);
645 }
646
647
648 static void
649 do_tab_text (struct tab_table *table, int c, int r, unsigned opt, char *text)
650 {
651   assert (c >= 0 );
652   assert (r >= 0 );
653   assert (c < tab_nc (table));
654   assert (r < tab_nr (table));
655
656 #if DEBUGGING
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))
660     {
661       printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
662               "(%d,%d)\n",
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));
666       return;
667     }
668 #endif
669
670   table->cc[c + r * table->cf] = ss_cstr (text);
671   table->ct[c + r * table->cf] = opt;
672 }
673
674 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
675    TEXT. */
676 void
677 tab_text (struct tab_table *table, int c, int r, unsigned opt,
678           const char *text)
679 {
680   do_tab_text (table, c, r, opt, pool_strdup (table->container, text));
681 }
682
683 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
684    FORMAT, which is formatted as if passed to printf. */
685 void
686 tab_text_format (struct tab_table *table, int c, int r, unsigned opt,
687                  const char *format, ...)
688 {
689   va_list args;
690
691   va_start (args, format);
692   do_tab_text (table, c, r, opt,
693                pool_vasprintf (table->container, format, args));
694   va_end (args);
695 }
696
697 static void
698 do_tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
699                    unsigned opt, char *text)
700 {
701   struct tab_joined_cell *j;
702
703   assert (x1 + table->col_ofs >= 0);
704   assert (y1 + table->row_ofs >= 0);
705   assert (y2 >= y1);
706   assert (x2 >= x1);
707   assert (y2 + table->row_ofs < tab_nr (table));
708   assert (x2 + table->col_ofs < tab_nc (table));
709
710 #if DEBUGGING
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))
715     {
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));
723       return;
724     }
725 #endif
726
727   tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
728
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);
735
736   opt |= TAB_JOIN;
737
738   {
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);
742
743     int y;
744
745     for (y = y1; y < y2; y++)
746       {
747         int x;
748
749         for (x = x1; x < x2; x++)
750           {
751             *cc++ = ss_buffer ((char *) j, 0);
752             *ct++ = opt;
753           }
754
755         cc += ofs;
756         ct += ofs;
757       }
758   }
759 }
760
761 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
762    options OPT to have text value TEXT. */
763 void
764 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
765                 unsigned opt, const char *text)
766 {
767   do_tab_joint_text (table, x1, y1, x2, y2, opt,
768                      pool_strdup (table->container, text));
769 }
770
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. */
774 void
775 tab_joint_text_format (struct tab_table *table, int x1, int y1, int x2, int y2,
776                        unsigned opt, const char *format, ...)
777 {
778   va_list args;
779
780   va_start (args, format);
781   do_tab_joint_text (table, x1, y1, x2, y2, opt,
782                      pool_vasprintf (table->container, format, args));
783   va_end (args);
784 }
785
786 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
787 void
788 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
789          struct substring *string)
790 {
791   assert (table != NULL && string != NULL);
792
793 #if DEBUGGING
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))
797     {
798       printf ("tab_raw(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
799               "(%d,%d)\n",
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));
803       return;
804     }
805 #endif
806
807   table->cc[c + r * table->cf] = *string;
808   table->ct[c + r * table->cf] = opt;
809 }
810 \f
811 /* Miscellaneous. */
812
813 /* Sets the widths of all the columns and heights of all the rows in
814    table T for driver D. */
815 static void
816 nowrap_dim (struct tab_rendering *r, void *aux UNUSED)
817 {
818   r->w[0] = tab_natural_width (r, 0);
819   r->h[0] = r->driver->font_height;
820 }
821
822 /* Sets the widths of all the columns and heights of all the rows in
823    table T for driver D. */
824 static void
825 wrap_dim (struct tab_rendering *r, void *aux UNUSED)
826 {
827   r->w[0] = tab_natural_width (r, 0);
828   r->h[0] = tab_natural_height (r, 0);
829 }
830
831 static void
832 do_tab_output_text (struct tab_table *t, int options, char *text)
833 {
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);
837   tab_submit (t);
838 }
839
840 /* Outputs TEXT as a table with a single cell having cell options
841    OPTIONS, which is a combination of the TAB_* and TAT_*
842    constants.  */
843 void
844 tab_output_text (int options, const char *text)
845 {
846   struct tab_table *table = tab_create (1, 1);
847   do_tab_output_text (table, options, pool_strdup (table->container, text));
848 }
849
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
853    printf. */
854 void
855 tab_output_text_format (int options, const char *format, ...)
856 {
857   struct tab_table *table;
858   va_list args;
859
860   table = tab_create (1, 1);
861
862   va_start (args, format);
863   do_tab_output_text (table, options,
864                       pool_vasprintf (table->container, format, args));
865   va_end (args);
866 }
867
868 /* Set table flags to FLAGS. */
869 void
870 tab_flags (struct tab_table *t, unsigned flags)
871 {
872   assert (t != NULL);
873   t->flags = flags;
874 }
875
876 /* Easy, type-safe way to submit a tab table to som. */
877 void
878 tab_submit (struct tab_table *t)
879 {
880   struct som_entity s;
881
882   assert (t != NULL);
883   s.class = &tab_table_class;
884   s.ext = t;
885   s.type = SOM_TABLE;
886   som_submit (&s);
887   tab_destroy (t);
888 }
889 \f
890 /* Editing. */
891
892 /* Set table row and column offsets for all functions that affect
893    cells or rules. */
894 void
895 tab_offset (struct tab_table *t, int col, int row)
896 {
897   int diff = 0;
898
899   assert (t != NULL);
900 #if DEBUGGING
901   if (row < -1 || row > tab_nr (t))
902     {
903       printf ("tab_offset(): row=%d in %d-row table\n", row, tab_nr (t));
904       NOT_REACHED ();
905     }
906   if (col < -1 || col > tab_nc (t))
907     {
908       printf ("tab_offset(): col=%d in %d-column table\n", col, tab_nc (t));
909       NOT_REACHED ();
910     }
911 #endif
912
913   if (row != -1)
914     diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
915   if (col != -1)
916     diff += (col - t->col_ofs), t->col_ofs = col;
917
918   t->cc += diff;
919   t->ct += diff;
920 }
921
922 /* Increment the row offset by one. If the table is too small,
923    increase its size. */
924 void
925 tab_next_row (struct tab_table *t)
926 {
927   assert (t != NULL);
928   t->cc += t->cf;
929   t->ct += t->cf;
930   if (++t->row_ofs >= tab_nr (t))
931     tab_realloc (t, -1, tab_nr (t) * 4 / 3);
932 }
933 \f
934 /* Return the number of columns and rows in the table into N_COLUMNS
935    and N_ROWS, respectively. */
936 static void
937 tabi_count (struct som_entity *t_, int *n_columns, int *n_rows)
938 {
939   struct tab_table *t = t_->ext;
940   *n_columns = t->nc;
941   *n_rows = t->nr;
942 }
943
944 /* Return the column style for this table into STYLE. */
945 static void
946 tabi_columns (struct som_entity *t_, int *style)
947 {
948   struct tab_table *t = t_->ext;
949   *style = t->col_style;
950 }
951
952 /* Return the number of header rows/columns on the left, right, top,
953    and bottom sides into HL, HR, HT, and HB, respectively. */
954 static void
955 tabi_headers (struct som_entity *t_, int *hl, int *hr, int *ht, int *hb)
956 {
957   struct tab_table *t = t_->ext;
958   *hl = t->l;
959   *hr = t->r;
960   *ht = t->t;
961   *hb = t->b;
962 }
963
964 /* Return flags set for the current table into FLAGS. */
965 static void
966 tabi_flags (struct som_entity *t_, unsigned *flags)
967 {
968   struct tab_table *t = t_->ext;
969   *flags = t->flags;
970 }
971
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)
976 {
977   switch (type)
978     {
979     case TAL_0:
980       return OUTP_L_NONE;
981     case TAL_GAP:
982     case TAL_1:
983       return OUTP_L_SINGLE;
984     case TAL_2:
985       return OUTP_L_DOUBLE;
986     default:
987       NOT_REACHED ();
988     }
989 }
990
991 static void *
992 tabi_render_init (struct som_entity *t_, struct outp_driver *driver,
993                   int hl, int hr, int ht, int hb)
994 {
995   const struct tab_table *t = t_->ext;
996   struct tab_rendering *r;
997   int col, row;
998   int i;
999
1000   tab_offset (t_->ext, 0, 0);
1001
1002   r = xmalloc (sizeof *r);
1003   r->table = t;
1004   r->driver = driver;
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);
1009   r->l = hl;
1010   r->r = hr;
1011   r->t = ht;
1012   r->b = hb;
1013
1014   /* Figure out sizes of rules. */
1015   for (row = 0; row <= tab_nr (t); row++)
1016     {
1017       int width = 0;
1018       for (col = 0; col < tab_nc (t); col++)
1019         {
1020           unsigned char rh = t->rh[col + row * t->cf];
1021           int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
1022           if (w > width)
1023             width = w;
1024         }
1025       r->hrh[row] = width;
1026     }
1027
1028   for (col = 0; col <= tab_nc (t); col++)
1029     {
1030       int width = 0;
1031       for (row = 0; row < tab_nr (t); row++)
1032         {
1033           unsigned char *rv = &t->rv[col + row * (t->cf + 1)];
1034           int w;
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)];
1038           if (w > width)
1039             width = w;
1040         }
1041       r->wrv[col] = width;
1042     }
1043
1044   /* Determine row heights and columns widths. */
1045   for (i = 0; i < tab_nr (t); i++)
1046     r->h[i] = -1;
1047   for (i = 0; i < tab_nc (t); i++)
1048     r->w[i] = -1;
1049
1050   t->dim (r, t->dim_aux);
1051
1052   for (i = 0; i < tab_nr (t); i++)
1053     if (r->h[i] < 0)
1054       error (0, 0, "height of table row %d is %d (not initialized?)",
1055              i, r->h[i]);
1056   for (i = 0; i < tab_nc (t); i++)
1057     if (r->w[i] < 0)
1058       error (0, 0, "width of table column %d is %d (not initialized?)",
1059              i, r->w[i]);
1060
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];
1070
1071   /* Title. */
1072   if (!(t->flags & SOMF_NO_TITLE))
1073     r->ht += driver->font_height;
1074
1075   return r;
1076 }
1077
1078 static void
1079 tabi_render_free (void *r_)
1080 {
1081   struct tab_rendering *r = r_;
1082
1083   free (r->w);
1084   free (r->h);
1085   free (r->hrh);
1086   free (r->wrv);
1087   free (r);
1088 }
1089
1090 /* Return the horizontal and vertical size of the entire table,
1091    including headers, for the current output device, into HORIZ and
1092    VERT. */
1093 static void
1094 tabi_area (void *r_, int *horiz, int *vert)
1095 {
1096   struct tab_rendering *r = r_;
1097   const struct tab_table *t = r->table;
1098   int width, col;
1099   int height, row;
1100
1101   width = 0;
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];
1105   *horiz = width;
1106
1107   height = 0;
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];
1111   *vert = height;
1112 }
1113
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)
1120    filled. */
1121 static void
1122 tabi_cumulate (void *r_, int cumtype, int start, int *end,
1123                int max, int *actual)
1124 {
1125   const struct tab_rendering *r = r_;
1126   const struct tab_table *t = r->table;
1127   int limit;
1128   int *cells, *rules;
1129   int total;
1130   int idx;
1131
1132   assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1133   if (cumtype == SOM_ROWS)
1134     {
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;
1140     }
1141   else
1142     {
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;
1148     }
1149
1150   total += *cells++;
1151   if (total > max)
1152     {
1153       if (end)
1154         *end = start;
1155       if (actual)
1156         *actual = 0;
1157       return;
1158     }
1159
1160   for (idx = start + 1; idx < limit; idx++)
1161     {
1162       int amt = *cells++ + *rules++;
1163
1164       total += amt;
1165       if (total > max)
1166         {
1167           total -= amt;
1168           break;
1169         }
1170     }
1171
1172   if (end)
1173     *end = idx;
1174
1175   if (actual)
1176     *actual = total;
1177 }
1178
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
1181    if Y is nonzero. */
1182 static void
1183 tabi_title (void *r_, int x, int y, int table_num, int subtable_num,
1184             const char *command_name)
1185 {
1186   const struct tab_rendering *r = r_;
1187   const struct tab_table *t = r->table;
1188   struct outp_text text;
1189   struct string title;
1190
1191   if (t->flags & SOMF_NO_TITLE)
1192     return;
1193
1194   ds_init_empty (&title);
1195   ds_put_format (&title,"%d.%d", table_num, subtable_num);
1196   if (x && y)
1197     ds_put_format (&title, "(%d:%d)", x, y);
1198   else if (x)
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);
1205
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;
1211   text.x = 0;
1212   text.y = r->driver->cp_y;
1213   r->driver->class->text_draw (r->driver, &text);
1214
1215   ds_destroy (&title);
1216 }
1217
1218 static int render_strip (const struct tab_rendering *,
1219                          int x, int y, int r, int c1, int c2, int r1, int r2);
1220
1221 static void
1222 add_range (int ranges[][2], int *np, int start, int end)
1223 {
1224   int n = *np;
1225   if (n == 0 || start > ranges[n - 1][1])
1226     {
1227       ranges[n][0] = start;
1228       ranges[n][1] = end;
1229       ++*np;
1230     }
1231   else
1232     ranges[n - 1][1] = end;
1233 }
1234
1235 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1236    current position on the current output device.  */
1237 static void
1238 tabi_render (void *r_, int c0, int r0, int c1, int r1)
1239 {
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;
1244   int y, i;
1245
1246   /* Rows to render, counting horizontal rules as rows.  */
1247   n_row_ranges = 0;
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);
1252
1253   /* Columns to render, counting vertical rules as columns. */
1254   n_col_ranges = 0;
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);
1258
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++)
1263     {
1264       int row;
1265
1266       for (row = rows[i][0]; row < rows[i][1]; row++)
1267         {
1268           int x, j;
1269
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]);
1275
1276           y += (row & 1) ? r->h[row / 2] : r->hrh[row / 2];
1277         }
1278     }
1279 }
1280
1281 const struct som_table_class tab_table_class =
1282   {
1283     tabi_count,
1284     tabi_columns,
1285     tabi_headers,
1286     tabi_flags,
1287
1288     tabi_render_init,
1289     tabi_render_free,
1290
1291     tabi_area,
1292     tabi_cumulate,
1293     tabi_title,
1294     tabi_render,
1295   };
1296 \f
1297 static enum outp_justification
1298 translate_justification (unsigned int opt)
1299 {
1300   switch (opt & TAB_ALIGN_MASK)
1301     {
1302     case TAB_RIGHT:
1303       return OUTP_RIGHT;
1304     case TAB_LEFT:
1305       return OUTP_LEFT;
1306     case TAB_CENTER:
1307       return OUTP_CENTER;
1308     default:
1309       NOT_REACHED ();
1310     }
1311 }
1312
1313 /* Returns the line style to use for drawing a rule of the given
1314    TYPE. */
1315 static enum outp_line_style
1316 rule_to_draw_type (unsigned char type)
1317 {
1318   switch (type)
1319     {
1320     case TAL_0:
1321     case TAL_GAP:
1322       return OUTP_L_NONE;
1323     case TAL_1:
1324       return OUTP_L_SINGLE;
1325     case TAL_2:
1326       return OUTP_L_DOUBLE;
1327     default:
1328       NOT_REACHED ();
1329     }
1330 }
1331
1332 /* Returns the horizontal rule at the given column and row. */
1333 static int
1334 get_hrule (const struct tab_table *t, int col, int row)
1335 {
1336   return t->rh[col + row * t->cf];
1337 }
1338
1339 /* Returns the vertical rule at the given column and row. */
1340 static int
1341 get_vrule (const struct tab_table *t, int col, int row)
1342 {
1343   return t->rv[col + row * (t->cf + 1)];
1344 }
1345
1346 /* Renders the horizontal rule at the given column and row
1347    at (X,Y) on the page. */
1348 static void
1349 render_horz_rule (const struct tab_rendering *r,
1350                   int x, int y, int col, int row)
1351 {
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);
1357 }
1358
1359 /* Renders the vertical rule at the given column and row
1360    at (X,Y) on the page. */
1361 static void
1362 render_vert_rule (const struct tab_rendering *r,
1363                   int x, int y, int col, int row)
1364 {
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);
1370 }
1371
1372 /* Renders the rule intersection at the given column and row
1373    at (X,Y) on the page. */
1374 static void
1375 render_rule_intersection (const struct tab_rendering *r,
1376                           int x, int y, int col, int row)
1377 {
1378   const struct tab_table *t = r->table;
1379
1380   /* Bounds of intersection. */
1381   int x0 = x;
1382   int y0 = y;
1383   int x1 = x + r->wrv[col];
1384   int y1 = y + r->hrh[row];
1385
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;
1391
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);
1397
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);
1402 }
1403
1404 /* Returns the width of columns C1...C2 exclusive,
1405    including interior but not exterior rules. */
1406 static int
1407 strip_width (const struct tab_rendering *r, int c1, int c2)
1408 {
1409   int width = 0;
1410   int c;
1411
1412   for (c = c1; c < c2; c++)
1413     width += r->w[c] + r->wrv[c + 1];
1414   if (c1 < c2)
1415     width -= r->wrv[c2];
1416   return width;
1417 }
1418
1419 /* Returns the height of rows R1...R2 exclusive,
1420    including interior but not exterior rules. */
1421 static int
1422 strip_height (const struct tab_rendering *r, int r1, int r2)
1423 {
1424   int height = 0;
1425   int row;
1426
1427   for (row = r1; row < r2; row++)
1428     height += r->h[row] + r->hrh[row + 1];
1429   if (r1 < r2)
1430     height -= r->hrh[r2];
1431   return height;
1432 }
1433
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. */
1437 static void
1438 render_cell (const struct tab_rendering *r,
1439              int x, int y, int col, int row, int c1, int r1)
1440 {
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];
1445
1446   if (!(type & TAB_JOIN))
1447     {
1448       if (!(type & TAB_EMPTY))
1449         {
1450           struct outp_text text;
1451           text.font = options_to_font (type);
1452           text.justification = translate_justification (type);
1453           text.string = *content;
1454           text.h = r->w[col];
1455           text.v = r->h[row];
1456           text.x = x;
1457           text.y = y;
1458           r->driver->class->text_draw (r->driver, &text);
1459         }
1460     }
1461   else
1462     {
1463       struct tab_joined_cell *j
1464         = (struct tab_joined_cell *) ss_data (*content);
1465
1466       if (j->x1 == col && j->y1 == row)
1467         {
1468           struct outp_text text;
1469           text.font = options_to_font (type);
1470           text.justification = translate_justification (type);
1471           text.string = j->contents;
1472           text.x = x;
1473           text.y = y;
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);
1477         }
1478     }
1479 }
1480
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. */
1487 static int
1488 render_strip (const struct tab_rendering *r,
1489               int x, int y, int row, int c0, int c1, int r0 UNUSED, int r1)
1490 {
1491   int col;
1492
1493   for (col = c0; col < c1; col++)
1494     if (col & 1)
1495       {
1496         if (row & 1)
1497           render_cell (r, x, y, col / 2, row / 2, c1 / 2, r1);
1498         else
1499           render_horz_rule (r, x, y, col / 2, row / 2);
1500         x += r->w[col / 2];
1501       }
1502     else
1503       {
1504         if (row & 1)
1505           render_vert_rule (r, x, y, col / 2, row / 2);
1506         else
1507           render_rule_intersection (r, x, y, col / 2, row / 2);
1508         x += r->wrv[col / 2];
1509       }
1510
1511   return x;
1512 }