f310319f590fe2833fa5828ccafac1b5efd917f8
[pspp] / src / output / tab.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2013, 2014 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 "output/tab.h"
20
21 #include <ctype.h>
22 #include <stdarg.h>
23 #include <limits.h>
24 #include <stdlib.h>
25
26 #include "data/data-out.h"
27 #include "data/format.h"
28 #include "data/settings.h"
29 #include "data/value.h"
30 #include "data/variable.h"
31 #include "libpspp/assertion.h"
32 #include "libpspp/compiler.h"
33 #include "libpspp/i18n.h"
34 #include "libpspp/misc.h"
35 #include "libpspp/pool.h"
36 #include "output/driver.h"
37 #include "output/table-item.h"
38 #include "output/table-provider.h"
39 #include "output/text-item.h"
40
41 #include "gl/minmax.h"
42 #include "gl/xalloc.h"
43
44 #include "gettext.h"
45 #define _(msgid) gettext (msgid)
46 \f
47 /* Set in the options field of cells that  */
48 #define TAB_JOIN (1u << TAB_FIRST_AVAILABLE)
49
50 /* Joined cell. */
51 struct tab_joined_cell
52   {
53     int d[TABLE_N_AXES][2];     /* Table region, same as struct table_cell. */
54     char *contents;
55   };
56
57 static const struct table_class tab_table_class;
58
59 /* Creates and returns a new table with NC columns and NR rows and initially no
60    header rows or columns.  The table's cells are initially empty. */
61 struct tab_table *
62 tab_create (int nc, int nr)
63 {
64   struct tab_table *t;
65
66   t = pool_create_container (struct tab_table, container);
67   table_init (&t->table, &tab_table_class);
68   table_set_nc (&t->table, nc);
69   table_set_nr (&t->table, nr);
70
71   t->title = NULL;
72   t->cf = nc;
73   t->cc = pool_calloc (t->container, nr * nc, sizeof *t->cc);
74   t->ct = pool_malloc (t->container, nr * nc);
75   memset (t->ct, 0, nc * nr);
76
77   t->rh = pool_nmalloc (t->container, nc, nr + 1);
78   memset (t->rh, 0, nc * (nr + 1));
79
80   t->rv = pool_nmalloc (t->container, nr, nc + 1);
81   memset (t->fmtmap, 0, sizeof (*t->fmtmap) * n_RC);
82
83   memset (t->rv, TAL_GAP, nr * (nc + 1));
84
85   t->fmtmap[RC_PVALUE] = &F_4_3;
86   t->fmtmap[RC_INTEGER] = &F_8_0;
87   t->fmtmap[RC_OTHER] = settings_get_format ();
88
89   t->col_ofs = t->row_ofs = 0;
90
91   return t;
92 }
93
94
95 void 
96 tab_set_format (struct tab_table *t, enum result_class rc, const struct fmt_spec *fmt)
97 {
98   t->fmtmap[rc] = fmt;
99 }
100
101
102 /* Sets the width and height of a table, in columns and rows,
103    respectively.  Use only to reduce the size of a table, since it
104    does not change the amount of allocated memory.
105
106    This function is obsolete.  Please do not add new uses of it.  (Instead, use
107    table_select() or one of its helper functions.) */
108 void
109 tab_resize (struct tab_table *t, int nc, int nr)
110 {
111   if (nc != -1)
112     {
113       assert (nc + t->col_ofs <= t->cf);
114       table_set_nc (&t->table, nc + t->col_ofs);
115     }
116   if (nr != -1)
117     {
118       assert (nr + t->row_ofs <= tab_nr (t));
119       table_set_nr (&t->table, nr + t->row_ofs);
120     }
121 }
122
123 /* Changes either or both dimensions of a table and reallocates memory as
124    necessary.
125
126    This function is obsolete.  Please do not add new uses of it.  (Instead, use
127    table_paste() or one of its helper functions to paste multiple tables
128    together into a larger one.) */
129 void
130 tab_realloc (struct tab_table *t, int nc, int nr)
131 {
132   int ro, co;
133
134   ro = t->row_ofs;
135   co = t->col_ofs;
136   if (ro || co)
137     tab_offset (t, 0, 0);
138
139   if (nc == -1)
140     nc = tab_nc (t);
141   if (nr == -1)
142     nr = tab_nr (t);
143
144   assert (nc == tab_nc (t));
145
146   if (nc > t->cf)
147     {
148       int mr1 = MIN (nr, tab_nr (t));
149       int mc1 = MIN (nc, tab_nc (t));
150
151       void **new_cc;
152       unsigned char *new_ct;
153       int r;
154
155       new_cc = pool_calloc (t->container, nr * nc, sizeof *new_cc);
156       new_ct = pool_malloc (t->container, nr * nc);
157       for (r = 0; r < mr1; r++)
158         {
159           memcpy (&new_cc[r * nc], &t->cc[r * tab_nc (t)], mc1 * sizeof *t->cc);
160           memcpy (&new_ct[r * nc], &t->ct[r * tab_nc (t)], mc1);
161           memset (&new_ct[r * nc + tab_nc (t)], 0, nc - tab_nc (t));
162         }
163       pool_free (t->container, t->cc);
164       pool_free (t->container, t->ct);
165       t->cc = new_cc;
166       t->ct = new_ct;
167       t->cf = nc;
168     }
169   else if (nr != tab_nr (t))
170     {
171       t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc);
172       t->ct = pool_realloc (t->container, t->ct, nr * nc);
173
174       t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
175       t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
176
177       if (nr > tab_nr (t))
178         {
179           memset (&t->rh[nc * (tab_nr (t) + 1)], TAL_0, (nr - tab_nr (t)) * nc);
180           memset (&t->rv[(nc + 1) * tab_nr (t)], TAL_GAP,
181                   (nr - tab_nr (t)) * (nc + 1));
182         }
183     }
184
185   memset (&t->ct[nc * tab_nr (t)], 0, nc * (nr - tab_nr (t)));
186   memset (&t->cc[nc * tab_nr (t)], 0, nc * (nr - tab_nr (t)) * sizeof *t->cc);
187
188   table_set_nr (&t->table, nr);
189   table_set_nc (&t->table, nc);
190
191   if (ro || co)
192     tab_offset (t, co, ro);
193 }
194
195 /* Sets the number of header rows on each side of TABLE to L on the
196    left, R on the right, T on the top, B on the bottom.  Header rows
197    are repeated when a table is broken across multiple columns or
198    multiple pages. */
199 void
200 tab_headers (struct tab_table *table, int l, int r, int t, int b)
201 {
202   table_set_hl (&table->table, l);
203   table_set_hr (&table->table, r);
204   table_set_ht (&table->table, t);
205   table_set_hb (&table->table, b);
206 }
207 \f
208 /* Rules. */
209
210 /* Draws a vertical line to the left of cells at horizontal position X
211    from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
212 void
213 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
214 {
215 #if DEBUGGING
216   if (x + t->col_ofs < 0 || x + t->col_ofs > tab_nc (t)
217       || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= tab_nr (t)
218       || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= tab_nr (t))
219     {
220       printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
221                 "table size (%d,%d)\n"),
222               x, t->col_ofs, x + t->col_ofs,
223               y1, t->row_ofs, y1 + t->row_ofs,
224               y2, t->row_ofs, y2 + t->row_ofs,
225               tab_nc (t), tab_nr (t));
226       return;
227     }
228 #endif
229
230   x += t->col_ofs;
231   y1 += t->row_ofs;
232   y2 += t->row_ofs;
233
234   assert (x >= 0);
235   assert (x <= tab_nc (t));
236   assert (y1 >= 0);
237   assert (y2 >= y1);
238   assert (y2 <= tab_nr (t));
239
240   if (style != -1)
241     {
242       int y;
243       for (y = y1; y <= y2; y++)
244         t->rv[x + (t->cf + 1) * y] = style;
245     }
246 }
247
248 /* Draws a horizontal line above cells at vertical position Y from X1
249    to X2 inclusive in style STYLE, if style is not -1. */
250 void
251 tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
252 {
253 #if DEBUGGING
254   if (y + t->row_ofs < 0 || y + t->row_ofs > tab_nr (t)
255       || x1 + t->col_ofs < 0 || x1 + t->col_ofs >= tab_nc (t)
256       || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= tab_nc (t))
257     {
258       printf (_("bad hline: x=(%d+%d=%d,%d+%d=%d) y=%d+%d=%d in "
259                 "table size (%d,%d)\n"),
260               x1, t->col_ofs, x1 + t->col_ofs,
261               x2, t->col_ofs, x2 + t->col_ofs,
262               y, t->row_ofs, y + t->row_ofs,
263               tab_nc (t), tab_nr (t));
264       return;
265     }
266 #endif
267
268   x1 += t->col_ofs;
269   x2 += t->col_ofs;
270   y += t->row_ofs;
271
272   assert (y >= 0);
273   assert (y <= tab_nr (t));
274   assert (x2 >= x1 );
275   assert (x1 >= 0 );
276   assert (x2 < tab_nc (t));
277
278   if (style != -1)
279     {
280       int x;
281       for (x = x1; x <= x2; x++)
282         t->rh[x + t->cf * y] = style;
283     }
284 }
285
286 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
287    lines of style F_H and vertical lines of style F_V.  Fills the
288    interior of the box with horizontal lines of style I_H and vertical
289    lines of style I_V.  Any of the line styles may be -1 to avoid
290    drawing those lines.  This is distinct from 0, which draws a null
291    line. */
292 void
293 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
294          int x1, int y1, int x2, int y2)
295 {
296 #if DEBUGGING
297   if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= tab_nc (t)
298       || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= tab_nc (t)
299       || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= tab_nr (t)
300       || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= tab_nr (t))
301     {
302       printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
303                 "in table size (%d,%d)\n"),
304               x1, t->col_ofs, x1 + t->col_ofs,
305               y1, t->row_ofs, y1 + t->row_ofs,
306               x2, t->col_ofs, x2 + t->col_ofs,
307               y2, t->row_ofs, y2 + t->row_ofs,
308               tab_nc (t), tab_nr (t));
309       NOT_REACHED ();
310     }
311 #endif
312
313   x1 += t->col_ofs;
314   x2 += t->col_ofs;
315   y1 += t->row_ofs;
316   y2 += t->row_ofs;
317
318   assert (x2 >= x1);
319   assert (y2 >= y1);
320   assert (x1 >= 0);
321   assert (y1 >= 0);
322   assert (x2 < tab_nc (t));
323   assert (y2 < tab_nr (t));
324
325   if (f_h != -1)
326     {
327       int x;
328       for (x = x1; x <= x2; x++)
329         {
330           t->rh[x + t->cf * y1] = f_h;
331           t->rh[x + t->cf * (y2 + 1)] = f_h;
332         }
333     }
334   if (f_v != -1)
335     {
336       int y;
337       for (y = y1; y <= y2; y++)
338         {
339           t->rv[x1 + (t->cf + 1) * y] = f_v;
340           t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
341         }
342     }
343
344   if (i_h != -1)
345     {
346       int y;
347
348       for (y = y1 + 1; y <= y2; y++)
349         {
350           int x;
351
352           for (x = x1; x <= x2; x++)
353             t->rh[x + t->cf * y] = i_h;
354         }
355     }
356   if (i_v != -1)
357     {
358       int x;
359
360       for (x = x1 + 1; x <= x2; x++)
361         {
362           int y;
363
364           for (y = y1; y <= y2; y++)
365             t->rv[x + (t->cf + 1) * y] = i_v;
366         }
367     }
368 }
369 \f
370 /* Cells. */
371
372 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
373    from V, displayed with format spec F. */
374 void
375 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
376            const union value *v, const struct variable *var,
377            const struct fmt_spec *f)
378 {
379   char *contents;
380
381 #if DEBUGGING
382   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
383       || c + table->col_ofs >= tab_nc (table)
384       || r + table->row_ofs >= tab_nr (table))
385     {
386       printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
387               "(%d,%d)\n",
388               c, table->col_ofs, c + table->col_ofs,
389               r, table->row_ofs, r + table->row_ofs,
390               tab_nc (table), tab_nr (table));
391       return;
392     }
393 #endif
394
395   contents = data_out_stretchy (v, var_get_encoding (var),
396                                 f != NULL ? f : var_get_print_format (var),
397                                 table->container);
398
399   table->cc[c + r * table->cf] = contents;
400   table->ct[c + r * table->cf] = opt;
401 }
402
403 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL as
404    formatted by FMT.
405    If FMT is null, then the default print format will be used.
406 */
407 void
408 tab_double (struct tab_table *table, int c, int r, unsigned char opt,
409             double val, const struct fmt_spec *fmt, enum result_class rc)
410 {
411   union value double_value ;
412   char *s;
413
414   assert (c >= 0);
415   assert (c < tab_nc (table));
416   assert (r >= 0);
417   assert (r < tab_nr (table));
418
419   if (fmt == NULL)
420     fmt = table->fmtmap[rc];
421   
422   fmt_check_output (fmt);
423
424 #if DEBUGGING
425   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
426       || c + table->col_ofs >= tab_nc (table)
427       || r + table->row_ofs >= tab_nr (table))
428     {
429       printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
430               "(%d,%d)\n",
431               c, table->col_ofs, c + table->col_ofs,
432               r, table->row_ofs, r + table->row_ofs,
433               tab_nc (table), tab_nr (table));
434       return;
435     }
436 #endif
437
438   double_value.f = val;
439   s = data_out_stretchy (&double_value, C_ENCODING, fmt, table->container);
440   table->cc[c + r * table->cf] = s + strspn (s, " ");
441   table->ct[c + r * table->cf] = opt;
442 }
443
444
445 static void
446 do_tab_text (struct tab_table *table, int c, int r, unsigned opt, char *text)
447 {
448   assert (c >= 0 );
449   assert (r >= 0 );
450   assert (c < tab_nc (table));
451   assert (r < tab_nr (table));
452
453 #if DEBUGGING
454   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
455       || c + table->col_ofs >= tab_nc (table)
456       || r + table->row_ofs >= tab_nr (table))
457     {
458       printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
459               "(%d,%d)\n",
460               c, table->col_ofs, c + table->col_ofs,
461               r, table->row_ofs, r + table->row_ofs,
462               tab_nc (table), tab_nr (table));
463       return;
464     }
465 #endif
466
467   table->cc[c + r * table->cf] = text;
468   table->ct[c + r * table->cf] = opt;
469 }
470
471 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
472    TEXT. */
473 void
474 tab_text (struct tab_table *table, int c, int r, unsigned opt,
475           const char *text)
476 {
477   do_tab_text (table, c, r, opt, pool_strdup (table->container, text));
478 }
479
480 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
481    FORMAT, which is formatted as if passed to printf. */
482 void
483 tab_text_format (struct tab_table *table, int c, int r, unsigned opt,
484                  const char *format, ...)
485 {
486   va_list args;
487
488   va_start (args, format);
489   do_tab_text (table, c, r, opt,
490                pool_vasprintf (table->container, format, args));
491   va_end (args);
492 }
493
494 static void
495 do_tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
496                    unsigned opt, char *text)
497 {
498   struct tab_joined_cell *j;
499
500   assert (x1 + table->col_ofs >= 0);
501   assert (y1 + table->row_ofs >= 0);
502   assert (y2 >= y1);
503   assert (x2 >= x1);
504   assert (y2 + table->row_ofs < tab_nr (table));
505   assert (x2 + table->col_ofs < tab_nc (table));
506
507 #if DEBUGGING
508   if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= tab_nc (table)
509       || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= tab_nr (table)
510       || x2 < x1 || x2 + table->col_ofs >= tab_nc (table)
511       || y2 < y2 || y2 + table->row_ofs >= tab_nr (table))
512     {
513       printf ("tab_joint_text(): bad cell "
514               "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
515               x1, table->col_ofs, x1 + table->col_ofs,
516               y1, table->row_ofs, y1 + table->row_ofs,
517               x2, table->col_ofs, x2 + table->col_ofs,
518               y2, table->row_ofs, y2 + table->row_ofs,
519               tab_nc (table), tab_nr (table));
520       return;
521     }
522 #endif
523
524   tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
525
526   j = pool_alloc (table->container, sizeof *j);
527   j->d[TABLE_HORZ][0] = x1 + table->col_ofs;
528   j->d[TABLE_VERT][0] = y1 + table->row_ofs;
529   j->d[TABLE_HORZ][1] = ++x2 + table->col_ofs;
530   j->d[TABLE_VERT][1] = ++y2 + table->row_ofs;
531   j->contents = text;
532
533   opt |= TAB_JOIN;
534
535   {
536     void **cc = &table->cc[x1 + y1 * table->cf];
537     unsigned char *ct = &table->ct[x1 + y1 * table->cf];
538     const int ofs = table->cf - (x2 - x1);
539
540     int y;
541
542     for (y = y1; y < y2; y++)
543       {
544         int x;
545
546         for (x = x1; x < x2; x++)
547           {
548             *cc++ = j;
549             *ct++ = opt;
550           }
551
552         cc += ofs;
553         ct += ofs;
554       }
555   }
556 }
557
558 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
559    options OPT to have text value TEXT. */
560 void
561 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
562                 unsigned opt, const char *text)
563 {
564   do_tab_joint_text (table, x1, y1, x2, y2, opt,
565                      pool_strdup (table->container, text));
566 }
567
568 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them
569    with options OPT to have text value FORMAT, which is formatted
570    as if passed to printf. */
571 void
572 tab_joint_text_format (struct tab_table *table, int x1, int y1, int x2, int y2,
573                        unsigned opt, const char *format, ...)
574 {
575   va_list args;
576
577   va_start (args, format);
578   do_tab_joint_text (table, x1, y1, x2, y2, opt,
579                      pool_vasprintf (table->container, format, args));
580   va_end (args);
581 }
582
583 bool
584 tab_cell_is_empty (const struct tab_table *table, int c, int r)
585 {
586   return table->cc[c + r * table->cf] == NULL;
587 }
588 \f
589 /* Miscellaneous. */
590
591 /* Set the title of table T to TITLE, which is formatted as if
592    passed to printf(). */
593 void
594 tab_title (struct tab_table *t, const char *title, ...)
595 {
596   va_list args;
597
598   free (t->title);
599   va_start (args, title);
600   t->title = xvasprintf (title, args);
601   va_end (args);
602 }
603
604 /* Easy, type-safe way to submit a tab table to som. */
605 void
606 tab_submit (struct tab_table *t)
607 {
608   table_item_submit (table_item_create (&t->table, t->title));
609 }
610 \f
611 /* Editing. */
612
613 /* Set table row and column offsets for all functions that affect
614    cells or rules. */
615 void
616 tab_offset (struct tab_table *t, int col, int row)
617 {
618   int diff = 0;
619
620 #if DEBUGGING
621   if (row < -1 || row > tab_nr (t))
622     {
623       printf ("tab_offset(): row=%d in %d-row table\n", row, tab_nr (t));
624       NOT_REACHED ();
625     }
626   if (col < -1 || col > tab_nc (t))
627     {
628       printf ("tab_offset(): col=%d in %d-column table\n", col, tab_nc (t));
629       NOT_REACHED ();
630     }
631 #endif
632
633   if (row != -1)
634     diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
635   if (col != -1)
636     diff += (col - t->col_ofs), t->col_ofs = col;
637
638   t->cc += diff;
639   t->ct += diff;
640 }
641
642 /* Increment the row offset by one. If the table is too small,
643    increase its size. */
644 void
645 tab_next_row (struct tab_table *t)
646 {
647   t->cc += t->cf;
648   t->ct += t->cf;
649   if (++t->row_ofs >= tab_nr (t))
650     tab_realloc (t, -1, tab_nr (t) * 4 / 3);
651 }
652 \f
653 /* Writes STRING to the output.  OPTIONS may be any valid combination of TAB_*
654    bits.
655
656    This function is obsolete.  Please do not add new uses of it.  Instead, use
657    a text_item (see output/text-item.h). */
658 void
659 tab_output_text (int options, const char *string)
660 {
661   enum text_item_type type = (options & TAB_EMPH ? TEXT_ITEM_SUBHEAD
662                               : options & TAB_FIX ? TEXT_ITEM_MONOSPACE
663                               : TEXT_ITEM_PARAGRAPH);
664   text_item_submit (text_item_create (type, string));
665 }
666
667 /* Same as tab_output_text(), but FORMAT is passed through printf-like
668    formatting before output. */
669 void
670 tab_output_text_format (int options, const char *format, ...)
671 {
672   va_list args;
673   char *text;
674
675   va_start (args, format);
676   text = xvasprintf (format, args);
677   va_end (args);
678
679   tab_output_text (options, text);
680
681   free (text);
682 }
683 \f
684 /* Table class implementation. */
685
686 static void
687 tab_destroy (struct table *table)
688 {
689   struct tab_table *t = tab_cast (table);
690   free (t->title);
691   t->title = NULL;
692   pool_destroy (t->container);
693 }
694
695 static void
696 tab_get_cell (const struct table *table, int x, int y, struct table_cell *cell)
697 {
698   const struct tab_table *t = tab_cast (table);
699   int index = x + y * t->cf;
700   unsigned char opt = t->ct[index];
701   const void *content = t->cc[index];
702
703   cell->options = opt;
704   if (opt & TAB_JOIN)
705     {
706       const struct tab_joined_cell *jc = content;
707       cell->d[TABLE_HORZ][0] = jc->d[TABLE_HORZ][0];
708       cell->d[TABLE_HORZ][1] = jc->d[TABLE_HORZ][1];
709       cell->d[TABLE_VERT][0] = jc->d[TABLE_VERT][0];
710       cell->d[TABLE_VERT][1] = jc->d[TABLE_VERT][1];
711       cell->contents = jc->contents;
712     }
713   else
714     {
715       cell->d[TABLE_HORZ][0] = x;
716       cell->d[TABLE_HORZ][1] = x + 1;
717       cell->d[TABLE_VERT][0] = y;
718       cell->d[TABLE_VERT][1] = y + 1;
719       cell->contents = content != NULL ? content : "";
720     }
721   cell->destructor = NULL;
722 }
723
724 static int
725 tab_get_rule (const struct table *table, enum table_axis axis, int x, int y)
726 {
727   const struct tab_table *t = tab_cast (table);
728   return (axis == TABLE_VERT
729           ? t->rh[x + t->cf * y]
730           : t->rv[x + (t->cf + 1) * y]);
731 }
732
733 static const struct table_class tab_table_class =
734   {
735     tab_destroy,
736     tab_get_cell,
737     tab_get_rule,
738     NULL,                       /* paste */
739     NULL,                       /* select */
740   };
741
742 struct tab_table *
743 tab_cast (const struct table *table)
744 {
745   assert (table->klass == &tab_table_class);
746   return UP_CAST (table, struct tab_table, table);
747 }