Add support for reading and writing SPV files.
[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 /* Frees CELL, which should have been initialized by calling
176    table_get_cell(). */
177 void
178 table_cell_free (struct table_cell *cell)
179 {
180   if (cell->destructor != NULL)
181     cell->destructor (cell->destructor_aux);
182 }
183
184 /* Returns one of the TAL_* enumeration constants (declared in output/table.h)
185    representing a rule running alongside one of the cells in TABLE.
186
187    Suppose NC is the number of columns in TABLE and NR is the number of rows.
188    Then, if AXIS is TABLE_HORZ, then 0 <= X <= NC and 0 <= Y < NR.  If (X,Y) =
189    (0,0), the return value is the rule that runs vertically on the left side of
190    cell (0,0); if (X,Y) = (1,0), it is the vertical rule between that cell and
191    cell (1,0); and so on, up to (NC,0), which runs vertically on the right of
192    cell (NC-1,0).
193
194    The following diagram illustrates the meaning of (X,Y) for AXIS = TABLE_HORZ
195    within a 7x7 table.  The '|' characters at the intersection of the X labels
196    and Y labels show the rule whose style would be returned by calling
197    table_get_rule with those X and Y values:
198
199                            0  1  2  3  4  5  6  7
200                            +--+--+--+--+--+--+--+
201                          0 |  |  |  |  |  |  |  |
202                            +--+--+--+--+--+--+--+
203                          1 |  |  |  |  |  |  |  |
204                            +--+--+--+--+--+--+--+
205                          2 |  |  |  |  |  |  |  |
206                            +--+--+--+--+--+--+--+
207                          3 |  |  |  |  |  |  |  |
208                            +--+--+--+--+--+--+--+
209                          4 |  |  |  |  |  |  |  |
210                            +--+--+--+--+--+--+--+
211                          5 |  |  |  |  |  |  |  |
212                            +--+--+--+--+--+--+--+
213                          6 |  |  |  |  |  |  |  |
214                            +--+--+--+--+--+--+--+
215
216    Similarly, if AXIS is TABLE_VERT, then 0 <= X < NC and 0 <= Y <= NR.  If
217    (X,Y) = (0,0), the return value is the rule that runs horizontally above
218    the top of cell (0,0); if (X,Y) = (0,1), it is the horizontal rule
219    between that cell and cell (0,1); and so on, up to (0,NR), which runs
220    horizontally below cell (0,NR-1). */
221 int
222 table_get_rule (const struct table *table, enum table_axis axis, int x, int y,
223                 struct cell_color *color)
224 {
225   assert (x >= 0 && x < table->n[TABLE_HORZ] + (axis == TABLE_HORZ));
226   assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT));
227   *color = (struct cell_color) CELL_COLOR_BLACK;
228   return table->klass->get_rule (table, axis, x, y, color);
229 }
230
231 void
232 table_cell_format_footnote_markers (const struct table_cell *cell,
233                                     struct string *s)
234 {
235   for (size_t i = 0; i < cell->n_footnotes; i++)
236     {
237       if (i)
238         ds_put_byte (s, ',');
239       ds_put_cstr (s, cell->footnotes[i]->marker);
240     }
241 }
242
243 static const struct footnote **
244 add_footnotes (const struct footnote **refs, size_t n_refs,
245                const struct footnote **footnotes, size_t *allocated, size_t *n)
246 {
247   for (size_t i = 0; i < n_refs; i++)
248     {
249       const struct footnote *f = refs[i];
250       if (f->idx >= *allocated)
251         {
252           size_t new_allocated = (f->idx + 1) * 2;
253           footnotes = xrealloc (footnotes, new_allocated * sizeof *footnotes);
254           while (*allocated < new_allocated)
255             footnotes[(*allocated)++] = NULL;
256         }
257       footnotes[f->idx] = f;
258       if (f->idx >= *n)
259         *n = f->idx + 1;
260     }
261   return footnotes;
262 }
263
264 size_t
265 table_collect_footnotes (const struct table_item *item,
266                          const struct footnote ***footnotesp)
267 {
268   const struct footnote **footnotes = NULL;
269   size_t allocated = 0;
270   size_t n = 0;
271
272   struct table *t = item->table;
273   for (int y = 0; y < table_nr (t); y++)
274     {
275       struct table_cell cell;
276       for (int x = 0; x < table_nc (t); x = cell.d[TABLE_HORZ][1])
277         {
278           table_get_cell (t, x, y, &cell);
279
280           if (x == cell.d[TABLE_HORZ][0] && y == cell.d[TABLE_VERT][0])
281             footnotes = add_footnotes (cell.footnotes, cell.n_footnotes,
282                                        footnotes, &allocated, &n);
283           table_cell_free (&cell);
284         }
285     }
286
287   const struct table_item_text *title = table_item_get_title (item);
288   if (title)
289     footnotes = add_footnotes (title->footnotes, title->n_footnotes,
290                                footnotes, &allocated, &n);
291
292   const struct table_item_layers *layers = table_item_get_layers (item);
293   if (layers)
294     {
295       for (size_t i = 0; i < layers->n_layers; i++)
296         footnotes = add_footnotes (layers->layers[i].footnotes,
297                                    layers->layers[i].n_footnotes,
298                                    footnotes, &allocated, &n);
299     }
300
301   const struct table_item_text *caption = table_item_get_caption (item);
302   if (caption)
303     footnotes = add_footnotes (caption->footnotes, caption->n_footnotes,
304                                footnotes, &allocated, &n);
305
306   size_t n_nonnull = 0;
307   for (size_t i = 0; i < n; i++)
308     if (footnotes[i])
309       footnotes[n_nonnull++] = footnotes[i];
310
311   *footnotesp = footnotes;
312   return n_nonnull;
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 }