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