table: Require headers to be set at table creation.
[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
35 #include "gl/xalloc.h"
36
37 /* Increases TABLE's reference count, indicating that it has an additional
38    owner.  An table that is shared among multiple owners must not be
39    modified. */
40 struct table *
41 table_ref (const struct table *table_)
42 {
43   struct table *table = CONST_CAST (struct table *, table_);
44   table->ref_cnt++;
45   return table;
46 }
47
48 /* Decreases TABLE's reference count, indicating that it has one fewer owner.
49    If TABLE no longer has any owners, it is freed. */
50 void
51 table_unref (struct table *table)
52 {
53   if (table != NULL)
54     {
55       assert (table->ref_cnt > 0);
56       if (--table->ref_cnt == 0)
57         tab_destroy (table);
58     }
59 }
60
61 /* Returns true if TABLE has more than one owner.  A table item that is shared
62    among multiple owners must not be modified. */
63 bool
64 table_is_shared (const struct table *table)
65 {
66   return table->ref_cnt > 1;
67 }
68 \f
69 struct area_style *
70 area_style_clone (struct pool *pool, const struct area_style *old)
71 {
72   struct area_style *new = pool_malloc (pool, sizeof *new);
73   *new = *old;
74   if (new->font_style.typeface)
75     new->font_style.typeface = pool_strdup (pool, new->font_style.typeface);
76   return new;
77 }
78
79 void
80 area_style_free (struct area_style *style)
81 {
82   if (style)
83     {
84       free (style->font_style.typeface);
85       free (style);
86     }
87 }
88
89 /* Initializes CELL with the contents of the table cell at column X and row Y
90    within TABLE.  When CELL is no longer needed, the caller is responsible for
91    freeing it by calling table_cell_free(CELL).
92
93    The caller must ensure that CELL is destroyed before TABLE is unref'ed. */
94 void
95 table_get_cell (const struct table *table, int x, int y,
96                 struct table_cell *cell)
97 {
98   assert (x >= 0 && x < table->n[TABLE_HORZ]);
99   assert (y >= 0 && y < table->n[TABLE_VERT]);
100
101   static const struct area_style default_style = AREA_STYLE_INITIALIZER;
102   cell->style = &default_style;
103
104   tab_get_cell (table, x, y, cell);
105 }
106
107 /* Returns one of the TAL_* enumeration constants (declared in output/table.h)
108    representing a rule running alongside one of the cells in TABLE.
109
110    Suppose NC is the number of columns in TABLE and NR is the number of rows.
111    Then, if AXIS is TABLE_HORZ, then 0 <= X <= NC and 0 <= Y < NR.  If (X,Y) =
112    (0,0), the return value is the rule that runs vertically on the left side of
113    cell (0,0); if (X,Y) = (1,0), it is the vertical rule between that cell and
114    cell (1,0); and so on, up to (NC,0), which runs vertically on the right of
115    cell (NC-1,0).
116
117    The following diagram illustrates the meaning of (X,Y) for AXIS = TABLE_HORZ
118    within a 7x7 table.  The '|' characters at the intersection of the X labels
119    and Y labels show the rule whose style would be returned by calling
120    table_get_rule with those X and Y values:
121
122                            0  1  2  3  4  5  6  7
123                            +--+--+--+--+--+--+--+
124                          0 |  |  |  |  |  |  |  |
125                            +--+--+--+--+--+--+--+
126                          1 |  |  |  |  |  |  |  |
127                            +--+--+--+--+--+--+--+
128                          2 |  |  |  |  |  |  |  |
129                            +--+--+--+--+--+--+--+
130                          3 |  |  |  |  |  |  |  |
131                            +--+--+--+--+--+--+--+
132                          4 |  |  |  |  |  |  |  |
133                            +--+--+--+--+--+--+--+
134                          5 |  |  |  |  |  |  |  |
135                            +--+--+--+--+--+--+--+
136                          6 |  |  |  |  |  |  |  |
137                            +--+--+--+--+--+--+--+
138
139    Similarly, if AXIS is TABLE_VERT, then 0 <= X < NC and 0 <= Y <= NR.  If
140    (X,Y) = (0,0), the return value is the rule that runs horizontally above
141    the top of cell (0,0); if (X,Y) = (0,1), it is the horizontal rule
142    between that cell and cell (0,1); and so on, up to (0,NR), which runs
143    horizontally below cell (0,NR-1). */
144 int
145 table_get_rule (const struct table *table, enum table_axis axis, int x, int y,
146                 struct cell_color *color)
147 {
148   assert (x >= 0 && x < table->n[TABLE_HORZ] + (axis == TABLE_HORZ));
149   assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT));
150   *color = (struct cell_color) CELL_COLOR_BLACK;
151   return tab_get_rule (table, axis, x, y, color);
152 }
153
154 void
155 table_cell_format_footnote_markers (const struct table_cell *cell,
156                                     struct string *s)
157 {
158   for (size_t i = 0; i < cell->n_footnotes; i++)
159     {
160       if (i)
161         ds_put_byte (s, ',');
162       ds_put_cstr (s, cell->footnotes[i]->marker);
163     }
164 }
165
166 static const struct footnote **
167 add_footnotes (const struct footnote **refs, size_t n_refs,
168                const struct footnote **footnotes, size_t *allocated, size_t *n)
169 {
170   for (size_t i = 0; i < n_refs; i++)
171     {
172       const struct footnote *f = refs[i];
173       if (f->idx >= *allocated)
174         {
175           size_t new_allocated = (f->idx + 1) * 2;
176           footnotes = xrealloc (footnotes, new_allocated * sizeof *footnotes);
177           while (*allocated < new_allocated)
178             footnotes[(*allocated)++] = NULL;
179         }
180       footnotes[f->idx] = f;
181       if (f->idx >= *n)
182         *n = f->idx + 1;
183     }
184   return footnotes;
185 }
186
187 size_t
188 table_collect_footnotes (const struct table_item *item,
189                          const struct footnote ***footnotesp)
190 {
191   const struct footnote **footnotes = NULL;
192   size_t allocated = 0;
193   size_t n = 0;
194
195   struct table *t = item->table;
196   for (int y = 0; y < table_nr (t); y++)
197     {
198       struct table_cell cell;
199       for (int x = 0; x < table_nc (t); x = cell.d[TABLE_HORZ][1])
200         {
201           table_get_cell (t, x, y, &cell);
202
203           if (x == cell.d[TABLE_HORZ][0] && y == cell.d[TABLE_VERT][0])
204             footnotes = add_footnotes (cell.footnotes, cell.n_footnotes,
205                                        footnotes, &allocated, &n);
206         }
207     }
208
209   const struct table_item_text *title = table_item_get_title (item);
210   if (title)
211     footnotes = add_footnotes (title->footnotes, title->n_footnotes,
212                                footnotes, &allocated, &n);
213
214   const struct table_item_layers *layers = table_item_get_layers (item);
215   if (layers)
216     {
217       for (size_t i = 0; i < layers->n_layers; i++)
218         footnotes = add_footnotes (layers->layers[i].footnotes,
219                                    layers->layers[i].n_footnotes,
220                                    footnotes, &allocated, &n);
221     }
222
223   const struct table_item_text *caption = table_item_get_caption (item);
224   if (caption)
225     footnotes = add_footnotes (caption->footnotes, caption->n_footnotes,
226                                footnotes, &allocated, &n);
227
228   size_t n_nonnull = 0;
229   for (size_t i = 0; i < n; i++)
230     if (footnotes[i])
231       footnotes[n_nonnull++] = footnotes[i];
232
233   *footnotesp = footnotes;
234   return n_nonnull;
235 }
236 \f
237 /* Returns a table that contains a single cell, whose contents are the
238    left-aligned TEXT.  */
239 struct table *
240 table_from_string (const char *text)
241 {
242   struct tab_table *t = tab_create (1, 1, 0, 0, 0, 0);
243   tab_text (t, 0, 0, TAB_LEFT, text);
244   return &t->table;
245 }
246 \f
247 const char *
248 table_halign_to_string (enum table_halign halign)
249 {
250   switch (halign)
251     {
252     case TABLE_HALIGN_LEFT: return "left";
253     case TABLE_HALIGN_CENTER: return "center";
254     case TABLE_HALIGN_RIGHT: return "right";
255     case TABLE_HALIGN_DECIMAL: return "decimal";
256     case TABLE_HALIGN_MIXED: return "mixed";
257     default: return "**error**";
258     }
259 }
260
261 const char *
262 table_valign_to_string (enum table_valign valign)
263 {
264   switch (valign)
265     {
266     case TABLE_VALIGN_TOP: return "top";
267     case TABLE_VALIGN_CENTER: return "center";
268     case TABLE_VALIGN_BOTTOM: return "bottom";
269     default: return "**error**";
270     }
271 }
272
273 enum table_halign
274 table_halign_interpret (enum table_halign halign, bool numeric)
275 {
276   switch (halign)
277     {
278     case TABLE_HALIGN_LEFT:
279     case TABLE_HALIGN_CENTER:
280     case TABLE_HALIGN_RIGHT:
281       return halign;
282
283     case TABLE_HALIGN_MIXED:
284       return numeric ? TABLE_HALIGN_RIGHT : TABLE_HALIGN_LEFT;
285
286     case TABLE_HALIGN_DECIMAL:
287       return TABLE_HALIGN_DECIMAL;
288
289     default:
290       NOT_REACHED ();
291     }
292 }
293
294 void
295 font_style_copy (struct font_style *dst, const struct font_style *src)
296 {
297   *dst = *src;
298   if (dst->typeface)
299     dst->typeface = xstrdup (dst->typeface);
300 }
301
302 void
303 font_style_uninit (struct font_style *font)
304 {
305   if (font)
306     free (font->typeface);
307 }
308
309 void
310 area_style_copy (struct area_style *dst, const struct area_style *src)
311 {
312   font_style_copy (&dst->font_style, &src->font_style);
313   dst->cell_style = src->cell_style;
314 }
315
316 void
317 area_style_uninit (struct area_style *area)
318 {
319   if (area)
320     font_style_uninit (&area->font_style);
321 }
322
323 const char *
324 table_stroke_to_string (enum table_stroke stroke)
325 {
326   switch (stroke)
327     {
328     case TABLE_STROKE_NONE: return "none";
329     case TABLE_STROKE_SOLID: return "solid";
330     case TABLE_STROKE_DASHED: return "dashed";
331     case TABLE_STROKE_THICK: return "thick";
332     case TABLE_STROKE_THIN: return "thin";
333     case TABLE_STROKE_DOUBLE: return "double";
334     default:
335       return "**error**";
336     }
337 }
338
339 void
340 cell_color_dump (const struct cell_color *c)
341 {
342   if (c->alpha != 255)
343     printf ("rgba(%d, %d, %d, %d)", c->r, c->g, c->b, c->alpha);
344   else
345     printf ("#%02"PRIx8"%02"PRIx8"%02"PRIx8, c->r, c->g, c->b);
346 }
347
348 void
349 font_style_dump (const struct font_style *f)
350 {
351   printf ("%s %dpx ", f->typeface, f->size);
352   cell_color_dump (&f->fg[0]);
353   putchar ('/');
354   cell_color_dump (&f->bg[0]);
355   if (!cell_color_equal (&f->fg[0], &f->fg[1])
356       || !cell_color_equal (&f->bg[0], &f->bg[1]))
357     {
358       printf (" alt=");
359       cell_color_dump (&f->fg[1]);
360       putchar ('/');
361       cell_color_dump (&f->bg[1]);
362     }
363   if (f->bold)
364     fputs (" bold", stdout);
365   if (f->italic)
366     fputs (" italic", stdout);
367   if (f->underline)
368     fputs (" underline", stdout);
369 }
370
371 void
372 cell_style_dump (const struct cell_style *c)
373 {
374   fputs (table_halign_to_string (c->halign), stdout);
375   if (c->halign == TABLE_HALIGN_DECIMAL)
376     printf ("(%.2gpx)", c->decimal_offset);
377   printf (" %s", table_valign_to_string (c->valign));
378   printf (" %d,%d,%d,%dpx",
379           c->margin[TABLE_HORZ][0], c->margin[TABLE_HORZ][1],
380           c->margin[TABLE_VERT][0], c->margin[TABLE_VERT][1]);
381 }