table: Add pool parameter to area_style_copy(), font_style_copy().
[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/table.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 = table_create (1, 1, 0, 0, 0, 0);
179   table_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 pool *container,
232                  struct font_style *dst, const struct font_style *src)
233 {
234   *dst = *src;
235   if (dst->typeface)
236     dst->typeface = pool_strdup (container, dst->typeface);
237 }
238
239 void
240 font_style_uninit (struct font_style *font)
241 {
242   if (font)
243     free (font->typeface);
244 }
245
246 void
247 area_style_copy (struct pool *container,
248                  struct area_style *dst, const struct area_style *src)
249 {
250   font_style_copy (container, &dst->font_style, &src->font_style);
251   dst->cell_style = src->cell_style;
252 }
253
254 void
255 area_style_uninit (struct area_style *area)
256 {
257   if (area)
258     font_style_uninit (&area->font_style);
259 }
260
261 const char *
262 table_stroke_to_string (enum table_stroke stroke)
263 {
264   switch (stroke)
265     {
266     case TABLE_STROKE_NONE: return "none";
267     case TABLE_STROKE_SOLID: return "solid";
268     case TABLE_STROKE_DASHED: return "dashed";
269     case TABLE_STROKE_THICK: return "thick";
270     case TABLE_STROKE_THIN: return "thin";
271     case TABLE_STROKE_DOUBLE: return "double";
272     default:
273       return "**error**";
274     }
275 }
276
277 void
278 cell_color_dump (const struct cell_color *c)
279 {
280   if (c->alpha != 255)
281     printf ("rgba(%d, %d, %d, %d)", c->r, c->g, c->b, c->alpha);
282   else
283     printf ("#%02"PRIx8"%02"PRIx8"%02"PRIx8, c->r, c->g, c->b);
284 }
285
286 void
287 font_style_dump (const struct font_style *f)
288 {
289   printf ("%s %dpx ", f->typeface, f->size);
290   cell_color_dump (&f->fg[0]);
291   putchar ('/');
292   cell_color_dump (&f->bg[0]);
293   if (!cell_color_equal (&f->fg[0], &f->fg[1])
294       || !cell_color_equal (&f->bg[0], &f->bg[1]))
295     {
296       printf (" alt=");
297       cell_color_dump (&f->fg[1]);
298       putchar ('/');
299       cell_color_dump (&f->bg[1]);
300     }
301   if (f->bold)
302     fputs (" bold", stdout);
303   if (f->italic)
304     fputs (" italic", stdout);
305   if (f->underline)
306     fputs (" underline", stdout);
307 }
308
309 void
310 cell_style_dump (const struct cell_style *c)
311 {
312   fputs (table_halign_to_string (c->halign), stdout);
313   if (c->halign == TABLE_HALIGN_DECIMAL)
314     printf ("(%.2gpx)", c->decimal_offset);
315   printf (" %s", table_valign_to_string (c->valign));
316   printf (" %d,%d,%d,%dpx",
317           c->margin[TABLE_HORZ][0], c->margin[TABLE_HORZ][1],
318           c->margin[TABLE_VERT][0], c->margin[TABLE_VERT][1]);
319 }
320 \f
321
322 static const bool debugging = true;
323
324 /* Joined cell. */
325 struct table_joined_cell
326 {
327   int d[TABLE_N_AXES][2];       /* Table region, same as struct table_cell. */
328   char *text;
329
330   size_t n_footnotes;
331   const struct footnote **footnotes;
332
333   const struct area_style *style;
334 };
335
336 /* Creates and returns a new table with NC columns and NR rows and initially no
337    header rows or columns.
338
339    Sets the number of header rows on each side of TABLE to HL on the
340    left, HR on the right, HT on the top, HB on the bottom.  Header rows
341    are repeated when a table is broken across multiple columns or
342    multiple pages.
343
344    The table's cells are initially empty. */
345 struct table *
346 table_create (int nc, int nr, int hl, int hr, int ht, int hb)
347 {
348   struct table *t;
349
350   t = pool_create_container (struct table, container);
351   t->n[TABLE_HORZ] = nc;
352   t->n[TABLE_VERT] = nr;
353   t->h[TABLE_HORZ][0] = hl;
354   t->h[TABLE_HORZ][1] = hr;
355   t->h[TABLE_VERT][0] = ht;
356   t->h[TABLE_VERT][1] = hb;
357   t->ref_cnt = 1;
358
359   t->cc = pool_calloc (t->container, nr * nc, sizeof *t->cc);
360   t->ct = pool_calloc (t->container, nr * nc, sizeof *t->ct);
361
362   t->rh = pool_nmalloc (t->container, nc, nr + 1);
363   memset (t->rh, TAL_0, nc * (nr + 1));
364
365   t->rv = pool_nmalloc (t->container, nr, nc + 1);
366   memset (t->rv, TAL_0, nr * (nc + 1));
367
368   memset (t->styles, 0, sizeof t->styles);
369   memset (t->rule_colors, 0, sizeof t->rule_colors);
370
371   return t;
372 }
373 \f
374 /* Rules. */
375
376 /* Draws a vertical line to the left of cells at horizontal position X
377    from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
378 void
379 table_vline (struct table *t, int style, int x, int y1, int y2)
380 {
381   if (debugging)
382     {
383       if (x < 0 || x > table_nc (t)
384           || y1 < 0 || y1 >= table_nr (t)
385           || y2 < 0 || y2 >= table_nr (t))
386         {
387           printf ("bad vline: x=%d y=(%d,%d) in table size (%d,%d)\n",
388                   x, y1, y2, table_nc (t), table_nr (t));
389           return;
390         }
391     }
392
393   assert (x >= 0);
394   assert (x <= table_nc (t));
395   assert (y1 >= 0);
396   assert (y2 >= y1);
397   assert (y2 <= table_nr (t));
398
399   if (style != -1)
400     {
401       int y;
402       for (y = y1; y <= y2; y++)
403         t->rv[x + (table_nc (t) + 1) * y] = style;
404     }
405 }
406
407 /* Draws a horizontal line above cells at vertical position Y from X1
408    to X2 inclusive in style STYLE, if style is not -1. */
409 void
410 table_hline (struct table *t, int style, int x1, int x2, int y)
411 {
412   if (debugging)
413     {
414       if (y < 0 || y > table_nr (t)
415           || x1 < 0 || x1 >= table_nc (t)
416           || x2 < 0 || x2 >= table_nc (t))
417         {
418           printf ("bad hline: x=(%d,%d) y=%d in table size (%d,%d)\n",
419                   x1, x2, y, table_nc (t), table_nr (t));
420           return;
421         }
422     }
423
424   assert (y >= 0);
425   assert (y <= table_nr (t));
426   assert (x2 >= x1);
427   assert (x1 >= 0);
428   assert (x2 < table_nc (t));
429
430   if (style != -1)
431     {
432       int x;
433       for (x = x1; x <= x2; x++)
434         t->rh[x + table_nc (t) * y] = style;
435     }
436 }
437
438 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
439    lines of style F_H and vertical lines of style F_V.  Fills the
440    interior of the box with horizontal lines of style I_H and vertical
441    lines of style I_V.  Any of the line styles may be -1 to avoid
442    drawing those lines.  This is distinct from 0, which draws a null
443    line. */
444 void
445 table_box (struct table *t, int f_h, int f_v, int i_h, int i_v,
446            int x1, int y1, int x2, int y2)
447 {
448   if (debugging)
449     {
450       if (x1 < 0 || x1 >= table_nc (t)
451           || x2 < 0 || x2 >= table_nc (t)
452           || y1 < 0 || y1 >= table_nr (t)
453           || y2 < 0 || y2 >= table_nr (t))
454         {
455           printf ("bad box: (%d,%d)-(%d,%d) in table size (%d,%d)\n",
456                   x1, y1, x2, y2, table_nc (t), table_nr (t));
457           NOT_REACHED ();
458         }
459     }
460
461   assert (x2 >= x1);
462   assert (y2 >= y1);
463   assert (x1 >= 0);
464   assert (y1 >= 0);
465   assert (x2 < table_nc (t));
466   assert (y2 < table_nr (t));
467
468   if (f_h != -1)
469     {
470       int x;
471       for (x = x1; x <= x2; x++)
472         {
473           t->rh[x + table_nc (t) * y1] = f_h;
474           t->rh[x + table_nc (t) * (y2 + 1)] = f_h;
475         }
476     }
477   if (f_v != -1)
478     {
479       int y;
480       for (y = y1; y <= y2; y++)
481         {
482           t->rv[x1 + (table_nc (t) + 1) * y] = f_v;
483           t->rv[(x2 + 1) + (table_nc (t) + 1) * y] = f_v;
484         }
485     }
486
487   if (i_h != -1)
488     {
489       int y;
490
491       for (y = y1 + 1; y <= y2; y++)
492         {
493           int x;
494
495           for (x = x1; x <= x2; x++)
496             t->rh[x + table_nc (t) * y] = i_h;
497         }
498     }
499   if (i_v != -1)
500     {
501       int x;
502
503       for (x = x1 + 1; x <= x2; x++)
504         {
505           int y;
506
507           for (y = y1; y <= y2; y++)
508             t->rv[x + (table_nc (t) + 1) * y] = i_v;
509         }
510     }
511 }
512 \f
513 /* Cells. */
514
515 static void
516 do_table_text (struct table *table, int c, int r, unsigned opt, char *text)
517 {
518   assert (c >= 0);
519   assert (r >= 0);
520   assert (c < table_nc (table));
521   assert (r < table_nr (table));
522
523   if (debugging)
524     {
525       if (c < 0 || r < 0 || c >= table_nc (table) || r >= table_nr (table))
526         {
527           printf ("table_text(): bad cell (%d,%d) in table size (%d,%d)\n",
528                   c, r, table_nc (table), table_nr (table));
529           return;
530         }
531     }
532
533   table->cc[c + r * table_nc (table)] = text;
534   table->ct[c + r * table_nc (table)] = opt;
535 }
536
537 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
538    TEXT. */
539 void
540 table_text (struct table *table, int c, int r, unsigned opt,
541           const char *text)
542 {
543   do_table_text (table, c, r, opt, pool_strdup (table->container, text));
544 }
545
546 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
547    FORMAT, which is formatted as if passed to printf. */
548 void
549 table_text_format (struct table *table, int c, int r, unsigned opt,
550                    const char *format, ...)
551 {
552   va_list args;
553
554   va_start (args, format);
555   do_table_text (table, c, r, opt,
556                  pool_vasprintf (table->container, format, args));
557   va_end (args);
558 }
559
560 static struct table_joined_cell *
561 add_joined_cell (struct table *table, int x1, int y1, int x2, int y2,
562                  unsigned opt)
563 {
564   struct table_joined_cell *j;
565
566   assert (x1 >= 0);
567   assert (y1 >= 0);
568   assert (y2 >= y1);
569   assert (x2 >= x1);
570   assert (y2 < table_nr (table));
571   assert (x2 < table_nc (table));
572
573   if (debugging)
574     {
575       if (x1 < 0 || x1 >= table_nc (table)
576           || y1 < 0 || y1 >= table_nr (table)
577           || x2 < x1 || x2 >= table_nc (table)
578           || y2 < y1 || y2 >= table_nr (table))
579         {
580           printf ("table_joint_text(): bad cell "
581                   "(%d,%d)-(%d,%d) in table size (%d,%d)\n",
582                   x1, y1, x2, y2, table_nc (table), table_nr (table));
583           return NULL;
584         }
585     }
586
587   table_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
588
589   j = pool_alloc (table->container, sizeof *j);
590   j->d[TABLE_HORZ][0] = x1;
591   j->d[TABLE_VERT][0] = y1;
592   j->d[TABLE_HORZ][1] = ++x2;
593   j->d[TABLE_VERT][1] = ++y2;
594   j->n_footnotes = 0;
595   j->footnotes = NULL;
596   j->style = NULL;
597
598   {
599     void **cc = &table->cc[x1 + y1 * table_nc (table)];
600     unsigned short *ct = &table->ct[x1 + y1 * table_nc (table)];
601     const int ofs = table_nc (table) - (x2 - x1);
602
603     int y;
604
605     for (y = y1; y < y2; y++)
606       {
607         int x;
608
609         for (x = x1; x < x2; x++)
610           {
611             *cc++ = j;
612             *ct++ = opt | TAB_JOIN;
613           }
614
615         cc += ofs;
616         ct += ofs;
617       }
618   }
619
620   return j;
621 }
622
623 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
624    options OPT to have text value TEXT. */
625 void
626 table_joint_text (struct table *table, int x1, int y1, int x2, int y2,
627                   unsigned opt, const char *text)
628 {
629   char *s = pool_strdup (table->container, text);
630   if (x1 == x2 && y1 == y2)
631     do_table_text (table, x1, y1, opt, s);
632   else
633     add_joined_cell (table, x1, y1, x2, y2, opt)->text = s;
634 }
635
636 struct footnote *
637 table_create_footnote (struct table *table, size_t idx, const char *content,
638                      const char *marker, struct area_style *style)
639 {
640   struct footnote *f = pool_alloc (table->container, sizeof *f);
641   f->idx = idx;
642   f->content = pool_strdup (table->container, content);
643   f->marker = pool_strdup (table->container, marker);
644   f->style = style;
645   return f;
646 }
647
648 void
649 table_add_footnote (struct table *table, int x, int y,
650                   const struct footnote *f)
651 {
652   int index = x + y * table_nc (table);
653   unsigned short opt = table->ct[index];
654   struct table_joined_cell *j;
655
656   if (opt & TAB_JOIN)
657     j = table->cc[index];
658   else
659     {
660       char *text = table->cc[index];
661
662       j = add_joined_cell (table, x, y, x, y, table->ct[index]);
663       j->text = text ? text : xstrdup ("");
664     }
665
666   j->footnotes = pool_realloc (table->container, j->footnotes,
667                                (j->n_footnotes + 1) * sizeof *j->footnotes);
668
669   j->footnotes[j->n_footnotes++] = f;
670 }
671
672 void
673 table_add_style (struct table *table, int x, int y,
674                  const struct area_style *style)
675 {
676   int index = x + y * table_nc (table);
677   unsigned short opt = table->ct[index];
678   struct table_joined_cell *j;
679
680   if (opt & TAB_JOIN)
681     j = table->cc[index];
682   else
683     {
684       char *text = table->cc[index];
685
686       j = add_joined_cell (table, x, y, x, y, table->ct[index]);
687       j->text = text ? text : xstrdup ("");
688     }
689
690   j->style = style;
691 }
692
693 bool
694 table_cell_is_empty (const struct table *table, int c, int r)
695 {
696   return table->cc[c + r * table_nc (table)] == NULL;
697 }
698 \f
699 /* Editing. */
700
701 /* Writes STRING to the output.  OPTIONS may be any valid combination of TAB_*
702    bits.
703
704    This function is obsolete.  Please do not add new uses of it.  Instead, use
705    a text_item (see output/text-item.h). */
706 void
707 table_output_text (int options UNUSED, const char *string)
708 {
709   text_item_submit (text_item_create (TEXT_ITEM_LOG, string));
710 }
711
712 /* Same as table_output_text(), but FORMAT is passed through printf-like
713    formatting before output. */
714 void
715 table_output_text_format (int options, const char *format, ...)
716 {
717   va_list args;
718   char *text;
719
720   va_start (args, format);
721   text = xvasprintf (format, args);
722   va_end (args);
723
724   table_output_text (options, text);
725
726   free (text);
727 }
728 \f
729 /* Initializes CELL with the contents of the table cell at column X and row Y
730    within TABLE.  When CELL is no longer needed, the caller is responsible for
731    freeing it by calling table_cell_free(CELL).
732
733    The caller must ensure that CELL is destroyed before TABLE is unref'ed. */
734 void
735 table_get_cell (const struct table *t, int x, int y, struct table_cell *cell)
736 {
737   assert (x >= 0 && x < t->n[TABLE_HORZ]);
738   assert (y >= 0 && y < t->n[TABLE_VERT]);
739
740   int index = x + y * table_nc (t);
741   unsigned short opt = t->ct[index];
742   const void *cc = t->cc[index];
743
744   cell->options = opt;
745   cell->n_footnotes = 0;
746
747   int style_idx = (opt & TAB_STYLE_MASK) >> TAB_STYLE_SHIFT;
748   const struct area_style *style = t->styles[style_idx];
749   if (style)
750     cell->style = style;
751   else
752     {
753       static const struct area_style styles[3][3] = {
754 #define S(H,V) [H][V] = { AREA_STYLE_INITIALIZER__,     \
755                           .cell_style.halign = H,       \
756                           .cell_style.valign = V }
757         S(TABLE_HALIGN_LEFT, TABLE_VALIGN_TOP),
758         S(TABLE_HALIGN_LEFT, TABLE_VALIGN_CENTER),
759         S(TABLE_HALIGN_LEFT, TABLE_VALIGN_BOTTOM),
760         S(TABLE_HALIGN_CENTER, TABLE_VALIGN_TOP),
761         S(TABLE_HALIGN_CENTER, TABLE_VALIGN_CENTER),
762         S(TABLE_HALIGN_CENTER, TABLE_VALIGN_BOTTOM),
763         S(TABLE_HALIGN_RIGHT, TABLE_VALIGN_TOP),
764         S(TABLE_HALIGN_RIGHT, TABLE_VALIGN_CENTER),
765         S(TABLE_HALIGN_RIGHT, TABLE_VALIGN_BOTTOM),
766       };
767
768       enum table_halign halign
769         = ((opt & TAB_HALIGN) == TAB_LEFT ? TABLE_HALIGN_LEFT
770            : (opt & TAB_HALIGN) == TAB_CENTER ? TABLE_HALIGN_CENTER
771            : TABLE_HALIGN_RIGHT);
772       enum table_valign valign
773         = ((opt & TAB_VALIGN) == TAB_TOP ? TABLE_VALIGN_TOP
774            : (opt & TAB_VALIGN) == TAB_MIDDLE ? TABLE_VALIGN_CENTER
775            : TABLE_VALIGN_BOTTOM);
776
777       cell->style = &styles[halign][valign];
778     }
779
780   if (opt & TAB_JOIN)
781     {
782       const struct table_joined_cell *jc = cc;
783       cell->text = jc->text;
784
785       cell->footnotes = jc->footnotes;
786       cell->n_footnotes = jc->n_footnotes;
787
788       cell->d[TABLE_HORZ][0] = jc->d[TABLE_HORZ][0];
789       cell->d[TABLE_HORZ][1] = jc->d[TABLE_HORZ][1];
790       cell->d[TABLE_VERT][0] = jc->d[TABLE_VERT][0];
791       cell->d[TABLE_VERT][1] = jc->d[TABLE_VERT][1];
792
793       if (jc->style)
794         cell->style = jc->style;
795     }
796   else
797     {
798       cell->d[TABLE_HORZ][0] = x;
799       cell->d[TABLE_HORZ][1] = x + 1;
800       cell->d[TABLE_VERT][0] = y;
801       cell->d[TABLE_VERT][1] = y + 1;
802       cell->text = CONST_CAST (char *, cc ? cc : "");
803     }
804 }
805
806 /* Returns one of the TAL_* enumeration constants (declared in output/table.h)
807    representing a rule running alongside one of the cells in TABLE.
808
809    Suppose NC is the number of columns in TABLE and NR is the number of rows.
810    Then, if AXIS is TABLE_HORZ, then 0 <= X <= NC and 0 <= Y < NR.  If (X,Y) =
811    (0,0), the return value is the rule that runs vertically on the left side of
812    cell (0,0); if (X,Y) = (1,0), it is the vertical rule between that cell and
813    cell (1,0); and so on, up to (NC,0), which runs vertically on the right of
814    cell (NC-1,0).
815
816    The following diagram illustrates the meaning of (X,Y) for AXIS = TABLE_HORZ
817    within a 7x7 table.  The '|' characters at the intersection of the X labels
818    and Y labels show the rule whose style would be returned by calling
819    table_get_rule with those X and Y values:
820
821                            0  1  2  3  4  5  6  7
822                            +--+--+--+--+--+--+--+
823                          0 |  |  |  |  |  |  |  |
824                            +--+--+--+--+--+--+--+
825                          1 |  |  |  |  |  |  |  |
826                            +--+--+--+--+--+--+--+
827                          2 |  |  |  |  |  |  |  |
828                            +--+--+--+--+--+--+--+
829                          3 |  |  |  |  |  |  |  |
830                            +--+--+--+--+--+--+--+
831                          4 |  |  |  |  |  |  |  |
832                            +--+--+--+--+--+--+--+
833                          5 |  |  |  |  |  |  |  |
834                            +--+--+--+--+--+--+--+
835                          6 |  |  |  |  |  |  |  |
836                            +--+--+--+--+--+--+--+
837
838    Similarly, if AXIS is TABLE_VERT, then 0 <= X < NC and 0 <= Y <= NR.  If
839    (X,Y) = (0,0), the return value is the rule that runs horizontally above
840    the top of cell (0,0); if (X,Y) = (0,1), it is the horizontal rule
841    between that cell and cell (0,1); and so on, up to (0,NR), which runs
842    horizontally below cell (0,NR-1). */
843 int
844 table_get_rule (const struct table *table, enum table_axis axis, int x, int y,
845                 struct cell_color *color)
846 {
847   assert (x >= 0 && x < table->n[TABLE_HORZ] + (axis == TABLE_HORZ));
848   assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT));
849
850   uint8_t raw = (axis == TABLE_VERT
851                  ? table->rh[x + table_nc (table) * y]
852                  : table->rv[x + (table_nc (table) + 1) * y]);
853   struct cell_color *p = table->rule_colors[(raw & TAB_RULE_STYLE_MASK)
854                                             >> TAB_RULE_STYLE_SHIFT];
855   *color = p ? *p : (struct cell_color) CELL_COLOR_BLACK;
856   return (raw & TAB_RULE_TYPE_MASK) >> TAB_RULE_TYPE_SHIFT;
857 }