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 *
table_ref(const struct table * 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
table_unref(struct table * table)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
table_is_shared(const struct table * table)65 table_is_shared (const struct table *table)
66 {
67   return table->ref_cnt > 1;
68 }
69 
70 struct area_style *
area_style_clone(struct pool * pool,const struct area_style * old)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
area_style_free(struct area_style * style)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
table_cell_format_footnote_markers(const struct table_cell * cell,struct string * s)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 **
add_footnotes(const struct footnote ** refs,size_t n_refs,const struct footnote ** footnotes,size_t * allocated,size_t * n)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
table_collect_footnotes(const struct table_item * item,const struct footnote *** footnotesp)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 
173 /* Returns a table that contains a single cell, whose contents are the
174    left-aligned TEXT.  */
175 struct table *
table_from_string(const char * text)176 table_from_string (const char *text)
177 {
178   struct table *t = table_create (1, 1, 0, 0, 0, 0);
179   t->styles[0] = pool_alloc (t->container, 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 
189 const char *
table_halign_to_string(enum table_halign halign)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 *
table_valign_to_string(enum table_valign valign)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
table_halign_interpret(enum table_halign halign,bool numeric)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
font_style_copy(struct pool * container,struct font_style * dst,const struct font_style * src)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
font_style_uninit(struct font_style * font)246 font_style_uninit (struct font_style *font)
247 {
248   if (font)
249     free (font->typeface);
250 }
251 
252 void
area_style_copy(struct pool * container,struct area_style * dst,const struct area_style * src)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
area_style_uninit(struct area_style * area)261 area_style_uninit (struct area_style *area)
262 {
263   if (area)
264     font_style_uninit (&area->font_style);
265 }
266 
267 const char *
table_stroke_to_string(enum table_stroke stroke)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
cell_color_dump(const struct cell_color * c)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
font_style_dump(const struct font_style * f)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
cell_style_dump(const struct cell_style * c)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 
327 
328 static const bool debugging = true;
329 
330 /* Creates and returns a new table with NC columns and NR rows and initially no
331    header rows or columns.
332 
333    Sets the number of header rows on each side of TABLE to HL on the
334    left, HR on the right, HT on the top, HB on the bottom.  Header rows
335    are repeated when a table is broken across multiple columns or
336    multiple pages.
337 
338    The table's cells are initially empty. */
339 struct table *
table_create(int nc,int nr,int hl,int hr,int ht,int hb)340 table_create (int nc, int nr, int hl, int hr, int ht, int hb)
341 {
342   struct table *t;
343 
344   t = pool_create_container (struct table, container);
345   t->n[TABLE_HORZ] = nc;
346   t->n[TABLE_VERT] = nr;
347   t->h[TABLE_HORZ][0] = hl;
348   t->h[TABLE_HORZ][1] = hr;
349   t->h[TABLE_VERT][0] = ht;
350   t->h[TABLE_VERT][1] = hb;
351   t->ref_cnt = 1;
352 
353   t->cc = pool_calloc (t->container, nr * nc, sizeof *t->cc);
354   t->ct = pool_calloc (t->container, nr * nc, sizeof *t->ct);
355 
356   t->rh = pool_nmalloc (t->container, nc, nr + 1);
357   memset (t->rh, TABLE_STROKE_NONE, nc * (nr + 1));
358 
359   t->rv = pool_nmalloc (t->container, nr, nc + 1);
360   memset (t->rv, TABLE_STROKE_NONE, nr * (nc + 1));
361 
362   memset (t->styles, 0, sizeof t->styles);
363   memset (t->rule_colors, 0, sizeof t->rule_colors);
364 
365   return t;
366 }
367 
368 /* Rules. */
369 
370 /* Draws a vertical line to the left of cells at horizontal position X
371    from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
372 void
table_vline(struct table * t,int style,int x,int y1,int y2)373 table_vline (struct table *t, int style, int x, int y1, int y2)
374 {
375   if (debugging)
376     {
377       if (x < 0 || x > table_nc (t)
378           || y1 < 0 || y1 >= table_nr (t)
379           || y2 < 0 || y2 >= table_nr (t))
380         {
381           printf ("bad vline: x=%d y=(%d,%d) in table size (%d,%d)\n",
382                   x, y1, y2, table_nc (t), table_nr (t));
383           return;
384         }
385     }
386 
387   assert (x >= 0);
388   assert (x <= table_nc (t));
389   assert (y1 >= 0);
390   assert (y2 >= y1);
391   assert (y2 <= table_nr (t));
392 
393   if (style != -1)
394     {
395       int y;
396       for (y = y1; y <= y2; y++)
397         t->rv[x + (table_nc (t) + 1) * y] = style;
398     }
399 }
400 
401 /* Draws a horizontal line above cells at vertical position Y from X1
402    to X2 inclusive in style STYLE, if style is not -1. */
403 void
table_hline(struct table * t,int style,int x1,int x2,int y)404 table_hline (struct table *t, int style, int x1, int x2, int y)
405 {
406   if (debugging)
407     {
408       if (y < 0 || y > table_nr (t)
409           || x1 < 0 || x1 >= table_nc (t)
410           || x2 < 0 || x2 >= table_nc (t))
411         {
412           printf ("bad hline: x=(%d,%d) y=%d in table size (%d,%d)\n",
413                   x1, x2, y, table_nc (t), table_nr (t));
414           return;
415         }
416     }
417 
418   assert (y >= 0);
419   assert (y <= table_nr (t));
420   assert (x2 >= x1);
421   assert (x1 >= 0);
422   assert (x2 < table_nc (t));
423 
424   if (style != -1)
425     {
426       int x;
427       for (x = x1; x <= x2; x++)
428         t->rh[x + table_nc (t) * y] = style;
429     }
430 }
431 
432 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
433    lines of style F_H and vertical lines of style F_V.  Fills the
434    interior of the box with horizontal lines of style I_H and vertical
435    lines of style I_V.  Any of the line styles may be -1 to avoid
436    drawing those lines.  This is distinct from 0, which draws a null
437    line. */
438 void
table_box(struct table * t,int f_h,int f_v,int i_h,int i_v,int x1,int y1,int x2,int y2)439 table_box (struct table *t, int f_h, int f_v, int i_h, int i_v,
440            int x1, int y1, int x2, int y2)
441 {
442   if (debugging)
443     {
444       if (x1 < 0 || x1 >= table_nc (t)
445           || x2 < 0 || x2 >= table_nc (t)
446           || y1 < 0 || y1 >= table_nr (t)
447           || y2 < 0 || y2 >= table_nr (t))
448         {
449           printf ("bad box: (%d,%d)-(%d,%d) in table size (%d,%d)\n",
450                   x1, y1, x2, y2, table_nc (t), table_nr (t));
451           NOT_REACHED ();
452         }
453     }
454 
455   assert (x2 >= x1);
456   assert (y2 >= y1);
457   assert (x1 >= 0);
458   assert (y1 >= 0);
459   assert (x2 < table_nc (t));
460   assert (y2 < table_nr (t));
461 
462   if (f_h != -1)
463     {
464       int x;
465       for (x = x1; x <= x2; x++)
466         {
467           t->rh[x + table_nc (t) * y1] = f_h;
468           t->rh[x + table_nc (t) * (y2 + 1)] = f_h;
469         }
470     }
471   if (f_v != -1)
472     {
473       int y;
474       for (y = y1; y <= y2; y++)
475         {
476           t->rv[x1 + (table_nc (t) + 1) * y] = f_v;
477           t->rv[(x2 + 1) + (table_nc (t) + 1) * y] = f_v;
478         }
479     }
480 
481   if (i_h != -1)
482     {
483       int y;
484 
485       for (y = y1 + 1; y <= y2; y++)
486         {
487           int x;
488 
489           for (x = x1; x <= x2; x++)
490             t->rh[x + table_nc (t) * y] = i_h;
491         }
492     }
493   if (i_v != -1)
494     {
495       int x;
496 
497       for (x = x1 + 1; x <= x2; x++)
498         {
499           int y;
500 
501           for (y = y1; y <= y2; y++)
502             t->rv[x + (table_nc (t) + 1) * y] = i_v;
503         }
504     }
505 }
506 
507 /* Cells. */
508 
509 static void
do_table_text(struct table * table,int c,int r,unsigned opt,char * text)510 do_table_text (struct table *table, int c, int r, unsigned opt, char *text)
511 {
512   assert (c >= 0);
513   assert (r >= 0);
514   assert (c < table_nc (table));
515   assert (r < table_nr (table));
516 
517   if (debugging)
518     {
519       if (c < 0 || r < 0 || c >= table_nc (table) || r >= table_nr (table))
520         {
521           printf ("table_text(): bad cell (%d,%d) in table size (%d,%d)\n",
522                   c, r, table_nc (table), table_nr (table));
523           return;
524         }
525     }
526 
527   table->cc[c + r * table_nc (table)] = text;
528   table->ct[c + r * table_nc (table)] = opt;
529 }
530 
531 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
532    TEXT. */
533 void
table_text(struct table * table,int c,int r,unsigned opt,const char * text)534 table_text (struct table *table, int c, int r, unsigned opt,
535           const char *text)
536 {
537   do_table_text (table, c, r, opt, pool_strdup (table->container, text));
538 }
539 
540 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
541    FORMAT, which is formatted as if passed to printf. */
542 void
table_text_format(struct table * table,int c,int r,unsigned opt,const char * format,...)543 table_text_format (struct table *table, int c, int r, unsigned opt,
544                    const char *format, ...)
545 {
546   va_list args;
547 
548   va_start (args, format);
549   do_table_text (table, c, r, opt,
550                  pool_vasprintf (table->container, format, args));
551   va_end (args);
552 }
553 
554 static struct table_cell *
add_joined_cell(struct table * table,int x1,int y1,int x2,int y2,unsigned opt)555 add_joined_cell (struct table *table, int x1, int y1, int x2, int y2,
556                  unsigned opt)
557 {
558   assert (x1 >= 0);
559   assert (y1 >= 0);
560   assert (y2 >= y1);
561   assert (x2 >= x1);
562   assert (y2 < table_nr (table));
563   assert (x2 < table_nc (table));
564 
565   if (debugging)
566     {
567       if (x1 < 0 || x1 >= table_nc (table)
568           || y1 < 0 || y1 >= table_nr (table)
569           || x2 < x1 || x2 >= table_nc (table)
570           || y2 < y1 || y2 >= table_nr (table))
571         {
572           printf ("table_joint_text(): bad cell "
573                   "(%d,%d)-(%d,%d) in table size (%d,%d)\n",
574                   x1, y1, x2, y2, table_nc (table), table_nr (table));
575           return NULL;
576         }
577     }
578 
579   table_box (table, -1, -1, TABLE_STROKE_NONE, TABLE_STROKE_NONE,
580              x1, y1, x2, y2);
581 
582   struct table_cell *cell = pool_alloc (table->container, sizeof *cell);
583   *cell = (struct table_cell) {
584     .d = { [TABLE_HORZ] = { x1, ++x2 },
585            [TABLE_VERT] = { y1, ++y2 } },
586     .options = opt,
587   };
588 
589   void **cc = &table->cc[x1 + y1 * table_nc (table)];
590   unsigned short *ct = &table->ct[x1 + y1 * table_nc (table)];
591   const int ofs = table_nc (table) - (x2 - x1);
592   for (int y = y1; y < y2; y++)
593     {
594       for (int x = x1; x < x2; x++)
595         {
596           *cc++ = cell;
597           *ct++ = opt | TAB_JOIN;
598         }
599 
600       cc += ofs;
601       ct += ofs;
602     }
603 
604   return cell;
605 }
606 
607 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
608    options OPT to have text value TEXT. */
609 void
table_joint_text(struct table * table,int x1,int y1,int x2,int y2,unsigned opt,const char * text)610 table_joint_text (struct table *table, int x1, int y1, int x2, int y2,
611                   unsigned opt, const char *text)
612 {
613   char *s = pool_strdup (table->container, text);
614   if (x1 == x2 && y1 == y2)
615     do_table_text (table, x1, y1, opt, s);
616   else
617     add_joined_cell (table, x1, y1, x2, y2, opt)->text = s;
618 }
619 
620 static struct table_cell *
get_joined_cell(struct table * table,int x,int y)621 get_joined_cell (struct table *table, int x, int y)
622 {
623   int index = x + y * table_nc (table);
624   unsigned short opt = table->ct[index];
625   struct table_cell *cell;
626 
627   if (opt & TAB_JOIN)
628     cell = table->cc[index];
629   else
630     {
631       char *text = table->cc[index];
632 
633       cell = add_joined_cell (table, x, y, x, y, table->ct[index]);
634       cell->text = text ? text : pool_strdup (table->container, "");
635     }
636   return cell;
637 }
638 
639 /* Sets the subscripts for column X, row Y in TABLE. */
640 void
table_add_subscripts(struct table * table,int x,int y,char ** subscripts,size_t n_subscripts)641 table_add_subscripts (struct table *table, int x, int y,
642                       char **subscripts, size_t n_subscripts)
643 {
644   struct table_cell *cell = get_joined_cell (table, x, y);
645 
646   cell->n_subscripts = n_subscripts;
647   cell->subscripts = pool_nalloc (table->container, n_subscripts,
648                                   sizeof *cell->subscripts);
649   for (size_t i = 0; i < n_subscripts; i++)
650     cell->subscripts[i] = pool_strdup (table->container, subscripts[i]);
651 }
652 
653 /* Sets the superscript for column X, row Y in TABLE. */
654 void
table_add_superscript(struct table * table,int x,int y,const char * superscript)655 table_add_superscript (struct table *table, int x, int y,
656                        const char *superscript)
657 {
658   get_joined_cell (table, x, y)->superscript
659     = pool_strdup (table->container, superscript);
660 }
661 
662 /* Create a footnote in TABLE with MARKER (e.g. "a") as its marker and CONTENT
663    as its content.  The footnote will be styled as STYLE, which is mandatory.
664    IDX must uniquely identify the footnote within TABLE.
665 
666    Returns the new footnote.  The return value is the only way to get to the
667    footnote later, so it is important for the caller to remember it. */
668 struct footnote *
table_create_footnote(struct table * table,size_t idx,const char * content,const char * marker,struct area_style * style)669 table_create_footnote (struct table *table, size_t idx, const char *content,
670                        const char *marker, struct area_style *style)
671 {
672   assert (style);
673 
674   struct footnote *f = pool_alloc (table->container, sizeof *f);
675   f->idx = idx;
676   f->content = pool_strdup (table->container, content);
677   f->marker = pool_strdup (table->container, marker);
678   f->style = style;
679   return f;
680 }
681 
682 /* Attaches a reference to footnote F to the cell at column X, row Y in
683    TABLE. */
684 void
table_add_footnote(struct table * table,int x,int y,const struct footnote * f)685 table_add_footnote (struct table *table, int x, int y,
686                     const struct footnote *f)
687 {
688   assert (f->style);
689 
690   struct table_cell *cell = get_joined_cell (table, x, y);
691 
692   cell->footnotes = pool_realloc (
693     table->container, cell->footnotes,
694     (cell->n_footnotes + 1) * sizeof *cell->footnotes);
695 
696   cell->footnotes[cell->n_footnotes++] = f;
697 }
698 
699 /* Overrides the style for column X, row Y in TABLE with STYLE.
700    Does not make a copy of STYLE, so it should either be allocated from
701    TABLE->container or have a lifetime that will outlive TABLE. */
702 void
table_add_style(struct table * table,int x,int y,const struct area_style * style)703 table_add_style (struct table *table, int x, int y,
704                  const struct area_style *style)
705 {
706   get_joined_cell (table, x, y)->style = style;
707 }
708 
709 /* Returns true if column C, row R has no contents, otherwise false. */
710 bool
table_cell_is_empty(const struct table * table,int c,int r)711 table_cell_is_empty (const struct table *table, int c, int r)
712 {
713   return table->cc[c + r * table_nc (table)] == NULL;
714 }
715 
716 /* Editing. */
717 
718 /* Writes STRING to the output.  OPTIONS may be any valid combination of TAB_*
719    bits.
720 
721    This function is obsolete.  Please do not add new uses of it.  Instead, use
722    a text_item (see output/text-item.h). */
723 void
table_output_text(int options UNUSED,const char * string)724 table_output_text (int options UNUSED, const char *string)
725 {
726   text_item_submit (text_item_create (TEXT_ITEM_LOG, string));
727 }
728 
729 /* Same as table_output_text(), but FORMAT is passed through printf-like
730    formatting before output. */
731 void
table_output_text_format(int options,const char * format,...)732 table_output_text_format (int options, const char *format, ...)
733 {
734   va_list args;
735   char *text;
736 
737   va_start (args, format);
738   text = xvasprintf (format, args);
739   va_end (args);
740 
741   table_output_text (options, text);
742 
743   free (text);
744 }
745 
746 /* Initializes CELL with the contents of the table cell at column X and row Y
747    within TABLE.  When CELL is no longer needed, the caller is responsible for
748    freeing it by calling table_cell_free(CELL).
749 
750    The caller must ensure that CELL is destroyed before TABLE is unref'ed. */
751 void
table_get_cell(const struct table * t,int x,int y,struct table_cell * cell)752 table_get_cell (const struct table *t, int x, int y, struct table_cell *cell)
753 {
754   assert (x >= 0 && x < t->n[TABLE_HORZ]);
755   assert (y >= 0 && y < t->n[TABLE_VERT]);
756 
757   int index = x + y * table_nc (t);
758   unsigned short opt = t->ct[index];
759   const void *cc = t->cc[index];
760 
761   const struct area_style *style
762     = t->styles[(opt & TAB_STYLE_MASK) >> TAB_STYLE_SHIFT];
763   if (opt & TAB_JOIN)
764     {
765       const struct table_cell *jc = cc;
766       *cell = *jc;
767       if (!cell->style)
768         cell->style = style;
769     }
770   else
771     *cell = (struct table_cell) {
772       .d = { [TABLE_HORZ] = { x, x + 1 },
773              [TABLE_VERT] = { y, y + 1 } },
774       .options = opt,
775       .text = CONST_CAST (char *, cc ? cc : ""),
776       .style = style,
777     };
778 
779   assert (cell->style);
780 }
781 
782 /* Returns one of the TAL_* enumeration constants (declared in output/table.h)
783    representing a rule running alongside one of the cells in TABLE.
784 
785    Suppose NC is the number of columns in TABLE and NR is the number of rows.
786    Then, if AXIS is TABLE_HORZ, then 0 <= X <= NC and 0 <= Y < NR.  If (X,Y) =
787    (0,0), the return value is the rule that runs vertically on the left side of
788    cell (0,0); if (X,Y) = (1,0), it is the vertical rule between that cell and
789    cell (1,0); and so on, up to (NC,0), which runs vertically on the right of
790    cell (NC-1,0).
791 
792    The following diagram illustrates the meaning of (X,Y) for AXIS = TABLE_HORZ
793    within a 7x7 table.  The '|' characters at the intersection of the X labels
794    and Y labels show the rule whose style would be returned by calling
795    table_get_rule with those X and Y values:
796 
797                            0  1  2  3  4  5  6  7
798                            +--+--+--+--+--+--+--+
799                          0 |  |  |  |  |  |  |  |
800                            +--+--+--+--+--+--+--+
801                          1 |  |  |  |  |  |  |  |
802                            +--+--+--+--+--+--+--+
803                          2 |  |  |  |  |  |  |  |
804                            +--+--+--+--+--+--+--+
805                          3 |  |  |  |  |  |  |  |
806                            +--+--+--+--+--+--+--+
807                          4 |  |  |  |  |  |  |  |
808                            +--+--+--+--+--+--+--+
809                          5 |  |  |  |  |  |  |  |
810                            +--+--+--+--+--+--+--+
811                          6 |  |  |  |  |  |  |  |
812                            +--+--+--+--+--+--+--+
813 
814    Similarly, if AXIS is TABLE_VERT, then 0 <= X < NC and 0 <= Y <= NR.  If
815    (X,Y) = (0,0), the return value is the rule that runs horizontally above
816    the top of cell (0,0); if (X,Y) = (0,1), it is the horizontal rule
817    between that cell and cell (0,1); and so on, up to (0,NR), which runs
818    horizontally below cell (0,NR-1). */
819 int
table_get_rule(const struct table * table,enum table_axis axis,int x,int y,struct cell_color * color)820 table_get_rule (const struct table *table, enum table_axis axis, int x, int y,
821                 struct cell_color *color)
822 {
823   assert (x >= 0 && x < table->n[TABLE_HORZ] + (axis == TABLE_HORZ));
824   assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT));
825 
826   uint8_t raw = (axis == TABLE_VERT
827                  ? table->rh[x + table_nc (table) * y]
828                  : table->rv[x + (table_nc (table) + 1) * y]);
829   struct cell_color *p = table->rule_colors[(raw & TAB_RULE_STYLE_MASK)
830                                             >> TAB_RULE_STYLE_SHIFT];
831   *color = p ? *p : (struct cell_color) CELL_COLOR_BLACK;
832   return (raw & TAB_RULE_TYPE_MASK) >> TAB_RULE_TYPE_SHIFT;
833 }
834