1e3c53d256d29b5c0a9c9900ad6a70fc131f958d
[pspp] / src / output / table.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2009, 2011, 2014, 2016 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/table.h"
20 #include "output/table-provider.h"
21
22 #include <assert.h>
23 #include <inttypes.h>
24 #include <stdlib.h>
25
26 #include "data/format.h"
27 #include "libpspp/assertion.h"
28 #include "libpspp/cast.h"
29 #include "libpspp/compiler.h"
30 #include "libpspp/pool.h"
31 #include "libpspp/str.h"
32 #include "output/table-item.h"
33 #include "output/tab.h"
34 #include "output/text-item.h"
35
36 #include "gl/xalloc.h"
37
38 /* Increases TABLE's reference count, indicating that it has an additional
39    owner.  An table that is shared among multiple owners must not be
40    modified. */
41 struct table *
42 table_ref (const struct table *table_)
43 {
44   struct table *table = CONST_CAST (struct table *, table_);
45   table->ref_cnt++;
46   return table;
47 }
48
49 /* Decreases TABLE's reference count, indicating that it has one fewer owner.
50    If TABLE no longer has any owners, it is freed. */
51 void
52 table_unref (struct table *table)
53 {
54   if (table != NULL)
55     {
56       assert (table->ref_cnt > 0);
57       if (--table->ref_cnt == 0)
58         pool_destroy (table->container);
59     }
60 }
61
62 /* Returns true if TABLE has more than one owner.  A table item that is shared
63    among multiple owners must not be modified. */
64 bool
65 table_is_shared (const struct table *table)
66 {
67   return table->ref_cnt > 1;
68 }
69 \f
70 struct area_style *
71 area_style_clone (struct pool *pool, const struct area_style *old)
72 {
73   struct area_style *new = pool_malloc (pool, sizeof *new);
74   *new = *old;
75   if (new->font_style.typeface)
76     new->font_style.typeface = pool_strdup (pool, new->font_style.typeface);
77   return new;
78 }
79
80 void
81 area_style_free (struct area_style *style)
82 {
83   if (style)
84     {
85       free (style->font_style.typeface);
86       free (style);
87     }
88 }
89
90 void
91 table_cell_format_footnote_markers (const struct table_cell *cell,
92                                     struct string *s)
93 {
94   for (size_t i = 0; i < cell->n_footnotes; i++)
95     {
96       if (i)
97         ds_put_byte (s, ',');
98       ds_put_cstr (s, cell->footnotes[i]->marker);
99     }
100 }
101
102 static const struct footnote **
103 add_footnotes (const struct footnote **refs, size_t n_refs,
104                const struct footnote **footnotes, size_t *allocated, size_t *n)
105 {
106   for (size_t i = 0; i < n_refs; i++)
107     {
108       const struct footnote *f = refs[i];
109       if (f->idx >= *allocated)
110         {
111           size_t new_allocated = (f->idx + 1) * 2;
112           footnotes = xrealloc (footnotes, new_allocated * sizeof *footnotes);
113           while (*allocated < new_allocated)
114             footnotes[(*allocated)++] = NULL;
115         }
116       footnotes[f->idx] = f;
117       if (f->idx >= *n)
118         *n = f->idx + 1;
119     }
120   return footnotes;
121 }
122
123 size_t
124 table_collect_footnotes (const struct table_item *item,
125                          const struct footnote ***footnotesp)
126 {
127   const struct footnote **footnotes = NULL;
128   size_t allocated = 0;
129   size_t n = 0;
130
131   struct table *t = item->table;
132   for (int y = 0; y < table_nr (t); y++)
133     {
134       struct table_cell cell;
135       for (int x = 0; x < table_nc (t); x = cell.d[TABLE_HORZ][1])
136         {
137           table_get_cell (t, x, y, &cell);
138
139           if (x == cell.d[TABLE_HORZ][0] && y == cell.d[TABLE_VERT][0])
140             footnotes = add_footnotes (cell.footnotes, cell.n_footnotes,
141                                        footnotes, &allocated, &n);
142         }
143     }
144
145   const struct table_item_text *title = table_item_get_title (item);
146   if (title)
147     footnotes = add_footnotes (title->footnotes, title->n_footnotes,
148                                footnotes, &allocated, &n);
149
150   const struct table_item_layers *layers = table_item_get_layers (item);
151   if (layers)
152     {
153       for (size_t i = 0; i < layers->n_layers; i++)
154         footnotes = add_footnotes (layers->layers[i].footnotes,
155                                    layers->layers[i].n_footnotes,
156                                    footnotes, &allocated, &n);
157     }
158
159   const struct table_item_text *caption = table_item_get_caption (item);
160   if (caption)
161     footnotes = add_footnotes (caption->footnotes, caption->n_footnotes,
162                                footnotes, &allocated, &n);
163
164   size_t n_nonnull = 0;
165   for (size_t i = 0; i < n; i++)
166     if (footnotes[i])
167       footnotes[n_nonnull++] = footnotes[i];
168
169   *footnotesp = footnotes;
170   return n_nonnull;
171 }
172 \f
173 /* Returns a table that contains a single cell, whose contents are the
174    left-aligned TEXT.  */
175 struct table *
176 table_from_string (const char *text)
177 {
178   struct table *t = tab_create (1, 1, 0, 0, 0, 0);
179   tab_text (t, 0, 0, TAB_LEFT, text);
180   return t;
181 }
182 \f
183 const char *
184 table_halign_to_string (enum table_halign halign)
185 {
186   switch (halign)
187     {
188     case TABLE_HALIGN_LEFT: return "left";
189     case TABLE_HALIGN_CENTER: return "center";
190     case TABLE_HALIGN_RIGHT: return "right";
191     case TABLE_HALIGN_DECIMAL: return "decimal";
192     case TABLE_HALIGN_MIXED: return "mixed";
193     default: return "**error**";
194     }
195 }
196
197 const char *
198 table_valign_to_string (enum table_valign valign)
199 {
200   switch (valign)
201     {
202     case TABLE_VALIGN_TOP: return "top";
203     case TABLE_VALIGN_CENTER: return "center";
204     case TABLE_VALIGN_BOTTOM: return "bottom";
205     default: return "**error**";
206     }
207 }
208
209 enum table_halign
210 table_halign_interpret (enum table_halign halign, bool numeric)
211 {
212   switch (halign)
213     {
214     case TABLE_HALIGN_LEFT:
215     case TABLE_HALIGN_CENTER:
216     case TABLE_HALIGN_RIGHT:
217       return halign;
218
219     case TABLE_HALIGN_MIXED:
220       return numeric ? TABLE_HALIGN_RIGHT : TABLE_HALIGN_LEFT;
221
222     case TABLE_HALIGN_DECIMAL:
223       return TABLE_HALIGN_DECIMAL;
224
225     default:
226       NOT_REACHED ();
227     }
228 }
229
230 void
231 font_style_copy (struct font_style *dst, const struct font_style *src)
232 {
233   *dst = *src;
234   if (dst->typeface)
235     dst->typeface = xstrdup (dst->typeface);
236 }
237
238 void
239 font_style_uninit (struct font_style *font)
240 {
241   if (font)
242     free (font->typeface);
243 }
244
245 void
246 area_style_copy (struct area_style *dst, const struct area_style *src)
247 {
248   font_style_copy (&dst->font_style, &src->font_style);
249   dst->cell_style = src->cell_style;
250 }
251
252 void
253 area_style_uninit (struct area_style *area)
254 {
255   if (area)
256     font_style_uninit (&area->font_style);
257 }
258
259 const char *
260 table_stroke_to_string (enum table_stroke stroke)
261 {
262   switch (stroke)
263     {
264     case TABLE_STROKE_NONE: return "none";
265     case TABLE_STROKE_SOLID: return "solid";
266     case TABLE_STROKE_DASHED: return "dashed";
267     case TABLE_STROKE_THICK: return "thick";
268     case TABLE_STROKE_THIN: return "thin";
269     case TABLE_STROKE_DOUBLE: return "double";
270     default:
271       return "**error**";
272     }
273 }
274
275 void
276 cell_color_dump (const struct cell_color *c)
277 {
278   if (c->alpha != 255)
279     printf ("rgba(%d, %d, %d, %d)", c->r, c->g, c->b, c->alpha);
280   else
281     printf ("#%02"PRIx8"%02"PRIx8"%02"PRIx8, c->r, c->g, c->b);
282 }
283
284 void
285 font_style_dump (const struct font_style *f)
286 {
287   printf ("%s %dpx ", f->typeface, f->size);
288   cell_color_dump (&f->fg[0]);
289   putchar ('/');
290   cell_color_dump (&f->bg[0]);
291   if (!cell_color_equal (&f->fg[0], &f->fg[1])
292       || !cell_color_equal (&f->bg[0], &f->bg[1]))
293     {
294       printf (" alt=");
295       cell_color_dump (&f->fg[1]);
296       putchar ('/');
297       cell_color_dump (&f->bg[1]);
298     }
299   if (f->bold)
300     fputs (" bold", stdout);
301   if (f->italic)
302     fputs (" italic", stdout);
303   if (f->underline)
304     fputs (" underline", stdout);
305 }
306
307 void
308 cell_style_dump (const struct cell_style *c)
309 {
310   fputs (table_halign_to_string (c->halign), stdout);
311   if (c->halign == TABLE_HALIGN_DECIMAL)
312     printf ("(%.2gpx)", c->decimal_offset);
313   printf (" %s", table_valign_to_string (c->valign));
314   printf (" %d,%d,%d,%dpx",
315           c->margin[TABLE_HORZ][0], c->margin[TABLE_HORZ][1],
316           c->margin[TABLE_VERT][0], c->margin[TABLE_VERT][1]);
317 }
318 \f
319
320 static const bool debugging = true;
321
322 /* Joined cell. */
323 struct tab_joined_cell
324 {
325   int d[TABLE_N_AXES][2];       /* Table region, same as struct table_cell. */
326   char *text;
327
328   size_t n_footnotes;
329   const struct footnote **footnotes;
330
331   const struct area_style *style;
332 };
333
334 /* Creates and returns a new table with NC columns and NR rows and initially no
335    header rows or columns.
336
337    Sets the number of header rows on each side of TABLE to HL on the
338    left, HR on the right, HT on the top, HB on the bottom.  Header rows
339    are repeated when a table is broken across multiple columns or
340    multiple pages.
341
342    The table's cells are initially empty. */
343 struct table *
344 tab_create (int nc, int nr, int hl, int hr, int ht, int hb)
345 {
346   struct table *t;
347
348   t = pool_create_container (struct table, container);
349   t->n[TABLE_HORZ] = nc;
350   t->n[TABLE_VERT] = nr;
351   t->h[TABLE_HORZ][0] = hl;
352   t->h[TABLE_HORZ][1] = hr;
353   t->h[TABLE_VERT][0] = ht;
354   t->h[TABLE_VERT][1] = hb;
355   t->ref_cnt = 1;
356
357   t->cc = pool_calloc (t->container, nr * nc, sizeof *t->cc);
358   t->ct = pool_calloc (t->container, nr * nc, sizeof *t->ct);
359
360   t->rh = pool_nmalloc (t->container, nc, nr + 1);
361   memset (t->rh, TAL_0, nc * (nr + 1));
362
363   t->rv = pool_nmalloc (t->container, nr, nc + 1);
364   memset (t->rv, TAL_0, nr * (nc + 1));
365
366   memset (t->styles, 0, sizeof t->styles);
367   memset (t->rule_colors, 0, sizeof t->rule_colors);
368
369   return t;
370 }
371 \f
372 /* Rules. */
373
374 /* Draws a vertical line to the left of cells at horizontal position X
375    from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
376 void
377 tab_vline (struct table *t, int style, int x, int y1, int y2)
378 {
379   if (debugging)
380     {
381       if (x < 0 || x > table_nc (t)
382           || y1 < 0 || y1 >= table_nr (t)
383           || y2 < 0 || y2 >= table_nr (t))
384         {
385           printf ("bad vline: x=%d y=(%d,%d) in table size (%d,%d)\n",
386                   x, y1, y2, table_nc (t), table_nr (t));
387           return;
388         }
389     }
390
391   assert (x >= 0);
392   assert (x <= table_nc (t));
393   assert (y1 >= 0);
394   assert (y2 >= y1);
395   assert (y2 <= table_nr (t));
396
397   if (style != -1)
398     {
399       int y;
400       for (y = y1; y <= y2; y++)
401         t->rv[x + (table_nc (t) + 1) * y] = style;
402     }
403 }
404
405 /* Draws a horizontal line above cells at vertical position Y from X1
406    to X2 inclusive in style STYLE, if style is not -1. */
407 void
408 tab_hline (struct table *t, int style, int x1, int x2, int y)
409 {
410   if (debugging)
411     {
412       if (y < 0 || y > table_nr (t)
413           || x1 < 0 || x1 >= table_nc (t)
414           || x2 < 0 || x2 >= table_nc (t))
415         {
416           printf ("bad hline: x=(%d,%d) y=%d in table size (%d,%d)\n",
417                   x1, x2, y, table_nc (t), table_nr (t));
418           return;
419         }
420     }
421
422   assert (y >= 0);
423   assert (y <= table_nr (t));
424   assert (x2 >= x1);
425   assert (x1 >= 0);
426   assert (x2 < table_nc (t));
427
428   if (style != -1)
429     {
430       int x;
431       for (x = x1; x <= x2; x++)
432         t->rh[x + table_nc (t) * y] = style;
433     }
434 }
435
436 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
437    lines of style F_H and vertical lines of style F_V.  Fills the
438    interior of the box with horizontal lines of style I_H and vertical
439    lines of style I_V.  Any of the line styles may be -1 to avoid
440    drawing those lines.  This is distinct from 0, which draws a null
441    line. */
442 void
443 tab_box (struct table *t, int f_h, int f_v, int i_h, int i_v,
444          int x1, int y1, int x2, int y2)
445 {
446   if (debugging)
447     {
448       if (x1 < 0 || x1 >= table_nc (t)
449           || x2 < 0 || x2 >= table_nc (t)
450           || y1 < 0 || y1 >= table_nr (t)
451           || y2 < 0 || y2 >= table_nr (t))
452         {
453           printf ("bad box: (%d,%d)-(%d,%d) in table size (%d,%d)\n",
454                   x1, y1, x2, y2, table_nc (t), table_nr (t));
455           NOT_REACHED ();
456         }
457     }
458
459   assert (x2 >= x1);
460   assert (y2 >= y1);
461   assert (x1 >= 0);
462   assert (y1 >= 0);
463   assert (x2 < table_nc (t));
464   assert (y2 < table_nr (t));
465
466   if (f_h != -1)
467     {
468       int x;
469       for (x = x1; x <= x2; x++)
470         {
471           t->rh[x + table_nc (t) * y1] = f_h;
472           t->rh[x + table_nc (t) * (y2 + 1)] = f_h;
473         }
474     }
475   if (f_v != -1)
476     {
477       int y;
478       for (y = y1; y <= y2; y++)
479         {
480           t->rv[x1 + (table_nc (t) + 1) * y] = f_v;
481           t->rv[(x2 + 1) + (table_nc (t) + 1) * y] = f_v;
482         }
483     }
484
485   if (i_h != -1)
486     {
487       int y;
488
489       for (y = y1 + 1; y <= y2; y++)
490         {
491           int x;
492
493           for (x = x1; x <= x2; x++)
494             t->rh[x + table_nc (t) * y] = i_h;
495         }
496     }
497   if (i_v != -1)
498     {
499       int x;
500
501       for (x = x1 + 1; x <= x2; x++)
502         {
503           int y;
504
505           for (y = y1; y <= y2; y++)
506             t->rv[x + (table_nc (t) + 1) * y] = i_v;
507         }
508     }
509 }
510 \f
511 /* Cells. */
512
513 static void
514 do_tab_text (struct table *table, int c, int r, unsigned opt, char *text)
515 {
516   assert (c >= 0);
517   assert (r >= 0);
518   assert (c < table_nc (table));
519   assert (r < table_nr (table));
520
521   if (debugging)
522     {
523       if (c < 0 || r < 0 || c >= table_nc (table) || r >= table_nr (table))
524         {
525           printf ("tab_text(): bad cell (%d,%d) in table size (%d,%d)\n",
526                   c, r, table_nc (table), table_nr (table));
527           return;
528         }
529     }
530
531   table->cc[c + r * table_nc (table)] = text;
532   table->ct[c + r * table_nc (table)] = opt;
533 }
534
535 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
536    TEXT. */
537 void
538 tab_text (struct table *table, int c, int r, unsigned opt,
539           const char *text)
540 {
541   do_tab_text (table, c, r, opt, pool_strdup (table->container, text));
542 }
543
544 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
545    FORMAT, which is formatted as if passed to printf. */
546 void
547 tab_text_format (struct table *table, int c, int r, unsigned opt,
548                  const char *format, ...)
549 {
550   va_list args;
551
552   va_start (args, format);
553   do_tab_text (table, c, r, opt,
554                pool_vasprintf (table->container, format, args));
555   va_end (args);
556 }
557
558 static struct tab_joined_cell *
559 add_joined_cell (struct table *table, int x1, int y1, int x2, int y2,
560                  unsigned opt)
561 {
562   struct tab_joined_cell *j;
563
564   assert (x1 >= 0);
565   assert (y1 >= 0);
566   assert (y2 >= y1);
567   assert (x2 >= x1);
568   assert (y2 < table_nr (table));
569   assert (x2 < table_nc (table));
570
571   if (debugging)
572     {
573       if (x1 < 0 || x1 >= table_nc (table)
574           || y1 < 0 || y1 >= table_nr (table)
575           || x2 < x1 || x2 >= table_nc (table)
576           || y2 < y1 || y2 >= table_nr (table))
577         {
578           printf ("tab_joint_text(): bad cell "
579                   "(%d,%d)-(%d,%d) in table size (%d,%d)\n",
580                   x1, y1, x2, y2, table_nc (table), table_nr (table));
581           return NULL;
582         }
583     }
584
585   tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
586
587   j = pool_alloc (table->container, sizeof *j);
588   j->d[TABLE_HORZ][0] = x1;
589   j->d[TABLE_VERT][0] = y1;
590   j->d[TABLE_HORZ][1] = ++x2;
591   j->d[TABLE_VERT][1] = ++y2;
592   j->n_footnotes = 0;
593   j->footnotes = NULL;
594   j->style = NULL;
595
596   {
597     void **cc = &table->cc[x1 + y1 * table_nc (table)];
598     unsigned short *ct = &table->ct[x1 + y1 * table_nc (table)];
599     const int ofs = table_nc (table) - (x2 - x1);
600
601     int y;
602
603     for (y = y1; y < y2; y++)
604       {
605         int x;
606
607         for (x = x1; x < x2; x++)
608           {
609             *cc++ = j;
610             *ct++ = opt | TAB_JOIN;
611           }
612
613         cc += ofs;
614         ct += ofs;
615       }
616   }
617
618   return j;
619 }
620
621 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
622    options OPT to have text value TEXT. */
623 void
624 tab_joint_text (struct table *table, int x1, int y1, int x2, int y2,
625                 unsigned opt, const char *text)
626 {
627   char *s = pool_strdup (table->container, text);
628   if (x1 == x2 && y1 == y2)
629     do_tab_text (table, x1, y1, opt, s);
630   else
631     add_joined_cell (table, x1, y1, x2, y2, opt)->text = s;
632 }
633
634 struct footnote *
635 tab_create_footnote (struct table *table, size_t idx, const char *content,
636                      const char *marker, struct area_style *style)
637 {
638   struct footnote *f = pool_alloc (table->container, sizeof *f);
639   f->idx = idx;
640   f->content = pool_strdup (table->container, content);
641   f->marker = pool_strdup (table->container, marker);
642   f->style = style;
643   return f;
644 }
645
646 void
647 tab_add_footnote (struct table *table, int x, int y,
648                   const struct footnote *f)
649 {
650   int index = x + y * table_nc (table);
651   unsigned short opt = table->ct[index];
652   struct tab_joined_cell *j;
653
654   if (opt & TAB_JOIN)
655     j = table->cc[index];
656   else
657     {
658       char *text = table->cc[index];
659
660       j = add_joined_cell (table, x, y, x, y, table->ct[index]);
661       j->text = text ? text : xstrdup ("");
662     }
663
664   j->footnotes = pool_realloc (table->container, j->footnotes,
665                                (j->n_footnotes + 1) * sizeof *j->footnotes);
666
667   j->footnotes[j->n_footnotes++] = f;
668 }
669
670 void
671 tab_add_style (struct table *table, int x, int y,
672                const struct area_style *style)
673 {
674   int index = x + y * table_nc (table);
675   unsigned short opt = table->ct[index];
676   struct tab_joined_cell *j;
677
678   if (opt & TAB_JOIN)
679     j = table->cc[index];
680   else
681     {
682       char *text = table->cc[index];
683
684       j = add_joined_cell (table, x, y, x, y, table->ct[index]);
685       j->text = text ? text : xstrdup ("");
686     }
687
688   j->style = style;
689 }
690
691 bool
692 tab_cell_is_empty (const struct table *table, int c, int r)
693 {
694   return table->cc[c + r * table_nc (table)] == NULL;
695 }
696 \f
697 /* Editing. */
698
699 /* Writes STRING to the output.  OPTIONS may be any valid combination of TAB_*
700    bits.
701
702    This function is obsolete.  Please do not add new uses of it.  Instead, use
703    a text_item (see output/text-item.h). */
704 void
705 tab_output_text (int options UNUSED, const char *string)
706 {
707   text_item_submit (text_item_create (TEXT_ITEM_LOG, string));
708 }
709
710 /* Same as tab_output_text(), but FORMAT is passed through printf-like
711    formatting before output. */
712 void
713 tab_output_text_format (int options, const char *format, ...)
714 {
715   va_list args;
716   char *text;
717
718   va_start (args, format);
719   text = xvasprintf (format, args);
720   va_end (args);
721
722   tab_output_text (options, text);
723
724   free (text);
725 }
726 \f
727 /* Initializes CELL with the contents of the table cell at column X and row Y
728    within TABLE.  When CELL is no longer needed, the caller is responsible for
729    freeing it by calling table_cell_free(CELL).
730
731    The caller must ensure that CELL is destroyed before TABLE is unref'ed. */
732 void
733 table_get_cell (const struct table *t, int x, int y, struct table_cell *cell)
734 {
735   assert (x >= 0 && x < t->n[TABLE_HORZ]);
736   assert (y >= 0 && y < t->n[TABLE_VERT]);
737
738   int index = x + y * table_nc (t);
739   unsigned short opt = t->ct[index];
740   const void *cc = t->cc[index];
741
742   cell->options = opt;
743   cell->n_footnotes = 0;
744
745   int style_idx = (opt & TAB_STYLE_MASK) >> TAB_STYLE_SHIFT;
746   const struct area_style *style = t->styles[style_idx];
747   if (style)
748     cell->style = style;
749   else
750     {
751       static const struct area_style styles[3][3] = {
752 #define S(H,V) [H][V] = { AREA_STYLE_INITIALIZER__,     \
753                           .cell_style.halign = H,       \
754                           .cell_style.valign = V }
755         S(TABLE_HALIGN_LEFT, TABLE_VALIGN_TOP),
756         S(TABLE_HALIGN_LEFT, TABLE_VALIGN_CENTER),
757         S(TABLE_HALIGN_LEFT, TABLE_VALIGN_BOTTOM),
758         S(TABLE_HALIGN_CENTER, TABLE_VALIGN_TOP),
759         S(TABLE_HALIGN_CENTER, TABLE_VALIGN_CENTER),
760         S(TABLE_HALIGN_CENTER, TABLE_VALIGN_BOTTOM),
761         S(TABLE_HALIGN_RIGHT, TABLE_VALIGN_TOP),
762         S(TABLE_HALIGN_RIGHT, TABLE_VALIGN_CENTER),
763         S(TABLE_HALIGN_RIGHT, TABLE_VALIGN_BOTTOM),
764       };
765
766       enum table_halign halign
767         = ((opt & TAB_HALIGN) == TAB_LEFT ? TABLE_HALIGN_LEFT
768            : (opt & TAB_HALIGN) == TAB_CENTER ? TABLE_HALIGN_CENTER
769            : TABLE_HALIGN_RIGHT);
770       enum table_valign valign
771         = ((opt & TAB_VALIGN) == TAB_TOP ? TABLE_VALIGN_TOP
772            : (opt & TAB_VALIGN) == TAB_MIDDLE ? TABLE_VALIGN_CENTER
773            : TABLE_VALIGN_BOTTOM);
774
775       cell->style = &styles[halign][valign];
776     }
777
778   if (opt & TAB_JOIN)
779     {
780       const struct tab_joined_cell *jc = cc;
781       cell->text = jc->text;
782
783       cell->footnotes = jc->footnotes;
784       cell->n_footnotes = jc->n_footnotes;
785
786       cell->d[TABLE_HORZ][0] = jc->d[TABLE_HORZ][0];
787       cell->d[TABLE_HORZ][1] = jc->d[TABLE_HORZ][1];
788       cell->d[TABLE_VERT][0] = jc->d[TABLE_VERT][0];
789       cell->d[TABLE_VERT][1] = jc->d[TABLE_VERT][1];
790
791       if (jc->style)
792         cell->style = jc->style;
793     }
794   else
795     {
796       cell->d[TABLE_HORZ][0] = x;
797       cell->d[TABLE_HORZ][1] = x + 1;
798       cell->d[TABLE_VERT][0] = y;
799       cell->d[TABLE_VERT][1] = y + 1;
800       cell->text = CONST_CAST (char *, cc ? cc : "");
801     }
802 }
803
804 /* Returns one of the TAL_* enumeration constants (declared in output/table.h)
805    representing a rule running alongside one of the cells in TABLE.
806
807    Suppose NC is the number of columns in TABLE and NR is the number of rows.
808    Then, if AXIS is TABLE_HORZ, then 0 <= X <= NC and 0 <= Y < NR.  If (X,Y) =
809    (0,0), the return value is the rule that runs vertically on the left side of
810    cell (0,0); if (X,Y) = (1,0), it is the vertical rule between that cell and
811    cell (1,0); and so on, up to (NC,0), which runs vertically on the right of
812    cell (NC-1,0).
813
814    The following diagram illustrates the meaning of (X,Y) for AXIS = TABLE_HORZ
815    within a 7x7 table.  The '|' characters at the intersection of the X labels
816    and Y labels show the rule whose style would be returned by calling
817    table_get_rule with those X and Y values:
818
819                            0  1  2  3  4  5  6  7
820                            +--+--+--+--+--+--+--+
821                          0 |  |  |  |  |  |  |  |
822                            +--+--+--+--+--+--+--+
823                          1 |  |  |  |  |  |  |  |
824                            +--+--+--+--+--+--+--+
825                          2 |  |  |  |  |  |  |  |
826                            +--+--+--+--+--+--+--+
827                          3 |  |  |  |  |  |  |  |
828                            +--+--+--+--+--+--+--+
829                          4 |  |  |  |  |  |  |  |
830                            +--+--+--+--+--+--+--+
831                          5 |  |  |  |  |  |  |  |
832                            +--+--+--+--+--+--+--+
833                          6 |  |  |  |  |  |  |  |
834                            +--+--+--+--+--+--+--+
835
836    Similarly, if AXIS is TABLE_VERT, then 0 <= X < NC and 0 <= Y <= NR.  If
837    (X,Y) = (0,0), the return value is the rule that runs horizontally above
838    the top of cell (0,0); if (X,Y) = (0,1), it is the horizontal rule
839    between that cell and cell (0,1); and so on, up to (0,NR), which runs
840    horizontally below cell (0,NR-1). */
841 int
842 table_get_rule (const struct table *table, enum table_axis axis, int x, int y,
843                 struct cell_color *color)
844 {
845   assert (x >= 0 && x < table->n[TABLE_HORZ] + (axis == TABLE_HORZ));
846   assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT));
847
848   uint8_t raw = (axis == TABLE_VERT
849                  ? table->rh[x + table_nc (table) * y]
850                  : table->rv[x + (table_nc (table) + 1) * y]);
851   struct cell_color *p = table->rule_colors[(raw & TAB_RULE_STYLE_MASK)
852                                             >> TAB_RULE_STYLE_SHIFT];
853   *color = p ? *p : (struct cell_color) CELL_COLOR_BLACK;
854   return (raw & TAB_RULE_TYPE_MASK) >> TAB_RULE_TYPE_SHIFT;
855 }