060da89a91bd5840ee7f38ec421428ddb05cc06f
[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         table->klass->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
69 /* Sets the number of left header columns in TABLE to HL. */
70 void
71 table_set_hl (struct table *table, int hl)
72 {
73   assert (!table_is_shared (table));
74   table->h[TABLE_HORZ][0] = hl;
75 }
76
77 /* Sets the number of right header columns in TABLE to HR. */
78 void
79 table_set_hr (struct table *table, int hr)
80 {
81   assert (!table_is_shared (table));
82   table->h[TABLE_HORZ][1] = hr;
83 }
84
85 /* Sets the number of top header rows in TABLE to HT. */
86 void
87 table_set_ht (struct table *table, int ht)
88 {
89   assert (!table_is_shared (table));
90   table->h[TABLE_VERT][0] = ht;
91 }
92
93 /* Sets the number of top header rows in TABLE to HB. */
94 void
95 table_set_hb (struct table *table, int hb)
96 {
97   assert (!table_is_shared (table));
98   table->h[TABLE_VERT][1] = hb;
99 }
100 \f
101 /* Initializes TABLE as a table of the specified CLASS, initially with a
102    reference count of 1.
103
104    TABLE initially has 0 rows and columns and no headers.  The table
105    implementation should update the numbers of rows and columns.  The table
106    implementation (or its client) may update the header rows and columns.
107
108    A table is an abstract class, that is, a plain struct table is not useful on
109    its own.  Thus, this function is normally called from the initialization
110    function of some subclass of table. */
111 void
112 table_init (struct table *table, const struct table_class *class)
113 {
114   table->klass = class;
115   table->n[TABLE_HORZ] = table->n[TABLE_VERT] = 0;
116   table->h[TABLE_HORZ][0] = table->h[TABLE_HORZ][1] = 0;
117   table->h[TABLE_VERT][0] = table->h[TABLE_VERT][1] = 0;
118   table->ref_cnt = 1;
119 }
120
121 /* Sets the number of columns in TABLE to NC. */
122 void
123 table_set_nc (struct table *table, int nc)
124 {
125   assert (!table_is_shared (table));
126   table->n[TABLE_HORZ] = nc;
127 }
128
129 /* Sets the number of rows in TABLE to NR. */
130 void
131 table_set_nr (struct table *table, int nr)
132 {
133   assert (!table_is_shared (table));
134   table->n[TABLE_VERT] = nr;
135 }
136 \f
137 struct area_style *
138 area_style_clone (struct pool *pool, const struct area_style *old)
139 {
140   struct area_style *new = pool_malloc (pool, sizeof *new);
141   *new = *old;
142   if (new->font_style.typeface)
143     new->font_style.typeface = pool_strdup (pool, new->font_style.typeface);
144   return new;
145 }
146
147 void
148 area_style_free (struct area_style *style)
149 {
150   if (style)
151     {
152       free (style->font_style.typeface);
153       free (style);
154     }
155 }
156
157 /* Initializes CELL with the contents of the table cell at column X and row Y
158    within TABLE.  When CELL is no longer needed, the caller is responsible for
159    freeing it by calling table_cell_free(CELL).
160
161    The caller must ensure that CELL is destroyed before TABLE is unref'ed. */
162 void
163 table_get_cell (const struct table *table, int x, int y,
164                 struct table_cell *cell)
165 {
166   assert (x >= 0 && x < table->n[TABLE_HORZ]);
167   assert (y >= 0 && y < table->n[TABLE_VERT]);
168
169   static const struct area_style default_style = AREA_STYLE_INITIALIZER;
170   cell->style = &default_style;
171
172   table->klass->get_cell (table, x, y, cell);
173 }
174
175 /* Returns one of the TAL_* enumeration constants (declared in output/table.h)
176    representing a rule running alongside one of the cells in TABLE.
177
178    Suppose NC is the number of columns in TABLE and NR is the number of rows.
179    Then, if AXIS is TABLE_HORZ, then 0 <= X <= NC and 0 <= Y < NR.  If (X,Y) =
180    (0,0), the return value is the rule that runs vertically on the left side of
181    cell (0,0); if (X,Y) = (1,0), it is the vertical rule between that cell and
182    cell (1,0); and so on, up to (NC,0), which runs vertically on the right of
183    cell (NC-1,0).
184
185    The following diagram illustrates the meaning of (X,Y) for AXIS = TABLE_HORZ
186    within a 7x7 table.  The '|' characters at the intersection of the X labels
187    and Y labels show the rule whose style would be returned by calling
188    table_get_rule with those X and Y values:
189
190                            0  1  2  3  4  5  6  7
191                            +--+--+--+--+--+--+--+
192                          0 |  |  |  |  |  |  |  |
193                            +--+--+--+--+--+--+--+
194                          1 |  |  |  |  |  |  |  |
195                            +--+--+--+--+--+--+--+
196                          2 |  |  |  |  |  |  |  |
197                            +--+--+--+--+--+--+--+
198                          3 |  |  |  |  |  |  |  |
199                            +--+--+--+--+--+--+--+
200                          4 |  |  |  |  |  |  |  |
201                            +--+--+--+--+--+--+--+
202                          5 |  |  |  |  |  |  |  |
203                            +--+--+--+--+--+--+--+
204                          6 |  |  |  |  |  |  |  |
205                            +--+--+--+--+--+--+--+
206
207    Similarly, if AXIS is TABLE_VERT, then 0 <= X < NC and 0 <= Y <= NR.  If
208    (X,Y) = (0,0), the return value is the rule that runs horizontally above
209    the top of cell (0,0); if (X,Y) = (0,1), it is the horizontal rule
210    between that cell and cell (0,1); and so on, up to (0,NR), which runs
211    horizontally below cell (0,NR-1). */
212 int
213 table_get_rule (const struct table *table, enum table_axis axis, int x, int y,
214                 struct cell_color *color)
215 {
216   assert (x >= 0 && x < table->n[TABLE_HORZ] + (axis == TABLE_HORZ));
217   assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT));
218   *color = (struct cell_color) CELL_COLOR_BLACK;
219   return table->klass->get_rule (table, axis, x, y, color);
220 }
221
222 void
223 table_cell_format_footnote_markers (const struct table_cell *cell,
224                                     struct string *s)
225 {
226   for (size_t i = 0; i < cell->n_footnotes; i++)
227     {
228       if (i)
229         ds_put_byte (s, ',');
230       ds_put_cstr (s, cell->footnotes[i]->marker);
231     }
232 }
233
234 static const struct footnote **
235 add_footnotes (const struct footnote **refs, size_t n_refs,
236                const struct footnote **footnotes, size_t *allocated, size_t *n)
237 {
238   for (size_t i = 0; i < n_refs; i++)
239     {
240       const struct footnote *f = refs[i];
241       if (f->idx >= *allocated)
242         {
243           size_t new_allocated = (f->idx + 1) * 2;
244           footnotes = xrealloc (footnotes, new_allocated * sizeof *footnotes);
245           while (*allocated < new_allocated)
246             footnotes[(*allocated)++] = NULL;
247         }
248       footnotes[f->idx] = f;
249       if (f->idx >= *n)
250         *n = f->idx + 1;
251     }
252   return footnotes;
253 }
254
255 size_t
256 table_collect_footnotes (const struct table_item *item,
257                          const struct footnote ***footnotesp)
258 {
259   const struct footnote **footnotes = NULL;
260   size_t allocated = 0;
261   size_t n = 0;
262
263   struct table *t = item->table;
264   for (int y = 0; y < table_nr (t); y++)
265     {
266       struct table_cell cell;
267       for (int x = 0; x < table_nc (t); x = cell.d[TABLE_HORZ][1])
268         {
269           table_get_cell (t, x, y, &cell);
270
271           if (x == cell.d[TABLE_HORZ][0] && y == cell.d[TABLE_VERT][0])
272             footnotes = add_footnotes (cell.footnotes, cell.n_footnotes,
273                                        footnotes, &allocated, &n);
274         }
275     }
276
277   const struct table_item_text *title = table_item_get_title (item);
278   if (title)
279     footnotes = add_footnotes (title->footnotes, title->n_footnotes,
280                                footnotes, &allocated, &n);
281
282   const struct table_item_layers *layers = table_item_get_layers (item);
283   if (layers)
284     {
285       for (size_t i = 0; i < layers->n_layers; i++)
286         footnotes = add_footnotes (layers->layers[i].footnotes,
287                                    layers->layers[i].n_footnotes,
288                                    footnotes, &allocated, &n);
289     }
290
291   const struct table_item_text *caption = table_item_get_caption (item);
292   if (caption)
293     footnotes = add_footnotes (caption->footnotes, caption->n_footnotes,
294                                footnotes, &allocated, &n);
295
296   size_t n_nonnull = 0;
297   for (size_t i = 0; i < n; i++)
298     if (footnotes[i])
299       footnotes[n_nonnull++] = footnotes[i];
300
301   *footnotesp = footnotes;
302   return n_nonnull;
303 }
304 \f
305 /* Returns a table that contains a single cell, whose contents are the
306    left-aligned TEXT.  */
307 struct table *
308 table_from_string (const char *text)
309 {
310   struct tab_table *t = tab_create (1, 1);
311   tab_text (t, 0, 0, TAB_LEFT, text);
312   return &t->table;
313 }
314 \f
315 const char *
316 table_halign_to_string (enum table_halign halign)
317 {
318   switch (halign)
319     {
320     case TABLE_HALIGN_LEFT: return "left";
321     case TABLE_HALIGN_CENTER: return "center";
322     case TABLE_HALIGN_RIGHT: return "right";
323     case TABLE_HALIGN_DECIMAL: return "decimal";
324     case TABLE_HALIGN_MIXED: return "mixed";
325     default: return "**error**";
326     }
327 }
328
329 const char *
330 table_valign_to_string (enum table_valign valign)
331 {
332   switch (valign)
333     {
334     case TABLE_VALIGN_TOP: return "top";
335     case TABLE_VALIGN_CENTER: return "center";
336     case TABLE_VALIGN_BOTTOM: return "bottom";
337     default: return "**error**";
338     }
339 }
340
341 enum table_halign
342 table_halign_interpret (enum table_halign halign, bool numeric)
343 {
344   switch (halign)
345     {
346     case TABLE_HALIGN_LEFT:
347     case TABLE_HALIGN_CENTER:
348     case TABLE_HALIGN_RIGHT:
349       return halign;
350
351     case TABLE_HALIGN_MIXED:
352       return numeric ? TABLE_HALIGN_RIGHT : TABLE_HALIGN_LEFT;
353
354     case TABLE_HALIGN_DECIMAL:
355       return TABLE_HALIGN_DECIMAL;
356
357     default:
358       NOT_REACHED ();
359     }
360 }
361
362 void
363 font_style_copy (struct font_style *dst, const struct font_style *src)
364 {
365   *dst = *src;
366   if (dst->typeface)
367     dst->typeface = xstrdup (dst->typeface);
368 }
369
370 void
371 font_style_uninit (struct font_style *font)
372 {
373   if (font)
374     free (font->typeface);
375 }
376
377 void
378 area_style_copy (struct area_style *dst, const struct area_style *src)
379 {
380   font_style_copy (&dst->font_style, &src->font_style);
381   dst->cell_style = src->cell_style;
382 }
383
384 void
385 area_style_uninit (struct area_style *area)
386 {
387   if (area)
388     font_style_uninit (&area->font_style);
389 }
390
391 const char *
392 table_stroke_to_string (enum table_stroke stroke)
393 {
394   switch (stroke)
395     {
396     case TABLE_STROKE_NONE: return "none";
397     case TABLE_STROKE_SOLID: return "solid";
398     case TABLE_STROKE_DASHED: return "dashed";
399     case TABLE_STROKE_THICK: return "thick";
400     case TABLE_STROKE_THIN: return "thin";
401     case TABLE_STROKE_DOUBLE: return "double";
402     default:
403       return "**error**";
404     }
405 }
406
407 void
408 cell_color_dump (const struct cell_color *c)
409 {
410   if (c->alpha != 255)
411     printf ("rgba(%d, %d, %d, %d)", c->r, c->g, c->b, c->alpha);
412   else
413     printf ("#%02"PRIx8"%02"PRIx8"%02"PRIx8, c->r, c->g, c->b);
414 }
415
416 void
417 font_style_dump (const struct font_style *f)
418 {
419   printf ("%s %dpx ", f->typeface, f->size);
420   cell_color_dump (&f->fg[0]);
421   putchar ('/');
422   cell_color_dump (&f->bg[0]);
423   if (!cell_color_equal (&f->fg[0], &f->fg[1])
424       || !cell_color_equal (&f->bg[0], &f->bg[1]))
425     {
426       printf (" alt=");
427       cell_color_dump (&f->fg[1]);
428       putchar ('/');
429       cell_color_dump (&f->bg[1]);
430     }
431   if (f->bold)
432     fputs (" bold", stdout);
433   if (f->italic)
434     fputs (" italic", stdout);
435   if (f->underline)
436     fputs (" underline", stdout);
437 }
438
439 void
440 cell_style_dump (const struct cell_style *c)
441 {
442   fputs (table_halign_to_string (c->halign), stdout);
443   if (c->halign == TABLE_HALIGN_DECIMAL)
444     printf ("(%.2gpx)", c->decimal_offset);
445   printf (" %s", table_valign_to_string (c->valign));
446   printf (" %d,%d,%d,%dpx",
447           c->margin[TABLE_HORZ][0], c->margin[TABLE_HORZ][1],
448           c->margin[TABLE_VERT][0], c->margin[TABLE_VERT][1]);
449 }