1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2009, 2011, 2014, 2016 Free Software Foundation, Inc.
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.
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.
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/>. */
19 #include "output/table.h"
20 #include "output/table-provider.h"
26 #include "libpspp/assertion.h"
27 #include "libpspp/cast.h"
28 #include "libpspp/compiler.h"
29 #include "libpspp/pool.h"
30 #include "libpspp/str.h"
31 #include "output/table-item.h"
33 #include "gl/xalloc.h"
35 /* Increases TABLE's reference count, indicating that it has an additional
36 owner. An table that is shared among multiple owners must not be
39 table_ref (const struct table *table_)
41 struct table *table = CONST_CAST (struct table *, table_);
46 /* Decreases TABLE's reference count, indicating that it has one fewer owner.
47 If TABLE no longer has any owners, it is freed. */
49 table_unref (struct table *table)
53 assert (table->ref_cnt > 0);
54 if (--table->ref_cnt == 0)
55 table->klass->destroy (table);
59 /* Returns true if TABLE has more than one owner. A table item that is shared
60 among multiple owners must not be modified. */
62 table_is_shared (const struct table *table)
64 return table->ref_cnt > 1;
67 /* Sets the number of left header columns in TABLE to HL. */
69 table_set_hl (struct table *table, int hl)
71 assert (!table_is_shared (table));
72 table->h[TABLE_HORZ][0] = hl;
75 /* Sets the number of right header columns in TABLE to HR. */
77 table_set_hr (struct table *table, int hr)
79 assert (!table_is_shared (table));
80 table->h[TABLE_HORZ][1] = hr;
83 /* Sets the number of top header rows in TABLE to HT. */
85 table_set_ht (struct table *table, int ht)
87 assert (!table_is_shared (table));
88 table->h[TABLE_VERT][0] = ht;
91 /* Sets the number of top header rows in TABLE to HB. */
93 table_set_hb (struct table *table, int hb)
95 assert (!table_is_shared (table));
96 table->h[TABLE_VERT][1] = hb;
99 /* Initializes TABLE as a table of the specified CLASS, initially with a
100 reference count of 1.
102 TABLE initially has 0 rows and columns and no headers. The table
103 implementation should update the numbers of rows and columns. The table
104 implementation (or its client) may update the header rows and columns.
106 A table is an abstract class, that is, a plain struct table is not useful on
107 its own. Thus, this function is normally called from the initialization
108 function of some subclass of table. */
110 table_init (struct table *table, const struct table_class *class)
112 table->klass = class;
113 table->n[TABLE_HORZ] = table->n[TABLE_VERT] = 0;
114 table->h[TABLE_HORZ][0] = table->h[TABLE_HORZ][1] = 0;
115 table->h[TABLE_VERT][0] = table->h[TABLE_VERT][1] = 0;
119 /* Sets the number of columns in TABLE to NC. */
121 table_set_nc (struct table *table, int nc)
123 assert (!table_is_shared (table));
124 table->n[TABLE_HORZ] = nc;
127 /* Sets the number of rows in TABLE to NR. */
129 table_set_nr (struct table *table, int nr)
131 assert (!table_is_shared (table));
132 table->n[TABLE_VERT] = nr;
136 area_style_clone (struct pool *pool, const struct area_style *old)
138 struct area_style *new = pool_malloc (pool, sizeof *new);
140 if (new->font_style.typeface)
141 new->font_style.typeface = pool_strdup (pool, new->font_style.typeface);
146 area_style_free (struct area_style *style)
150 free (style->font_style.typeface);
155 /* Initializes CELL with the contents of the table cell at column X and row Y
156 within TABLE. When CELL is no longer needed, the caller is responsible for
157 freeing it by calling table_cell_free(CELL).
159 The caller must ensure that CELL is destroyed before TABLE is unref'ed. */
161 table_get_cell (const struct table *table, int x, int y,
162 struct table_cell *cell)
164 assert (x >= 0 && x < table->n[TABLE_HORZ]);
165 assert (y >= 0 && y < table->n[TABLE_VERT]);
167 static const struct area_style default_style = AREA_STYLE_INITIALIZER;
168 cell->style = &default_style;
170 table->klass->get_cell (table, x, y, cell);
173 /* Frees CELL, which should have been initialized by calling
176 table_cell_free (struct table_cell *cell)
178 if (cell->destructor != NULL)
179 cell->destructor (cell->destructor_aux);
182 /* Returns one of the TAL_* enumeration constants (declared in output/table.h)
183 representing a rule running alongside one of the cells in TABLE.
185 Suppose NC is the number of columns in TABLE and NR is the number of rows.
186 Then, if AXIS is TABLE_HORZ, then 0 <= X <= NC and 0 <= Y < NR. If (X,Y) =
187 (0,0), the return value is the rule that runs vertically on the left side of
188 cell (0,0); if (X,Y) = (1,0), it is the vertical rule between that cell and
189 cell (1,0); and so on, up to (NC,0), which runs vertically on the right of
192 The following diagram illustrates the meaning of (X,Y) for AXIS = TABLE_HORZ
193 within a 7x7 table. The '|' characters at the intersection of the X labels
194 and Y labels show the rule whose style would be returned by calling
195 table_get_rule with those X and Y values:
198 +--+--+--+--+--+--+--+
200 +--+--+--+--+--+--+--+
202 +--+--+--+--+--+--+--+
204 +--+--+--+--+--+--+--+
206 +--+--+--+--+--+--+--+
208 +--+--+--+--+--+--+--+
210 +--+--+--+--+--+--+--+
212 +--+--+--+--+--+--+--+
214 Similarly, if AXIS is TABLE_VERT, then 0 <= X < NC and 0 <= Y <= NR. If
215 (X,Y) = (0,0), the return value is the rule that runs horizontally above
216 the top of cell (0,0); if (X,Y) = (0,1), it is the horizontal rule
217 between that cell and cell (0,1); and so on, up to (0,NR), which runs
218 horizontally below cell (0,NR-1). */
220 table_get_rule (const struct table *table, enum table_axis axis, int x, int y,
221 struct cell_color *color)
223 assert (x >= 0 && x < table->n[TABLE_HORZ] + (axis == TABLE_HORZ));
224 assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT));
225 *color = (struct cell_color) CELL_COLOR_BLACK;
226 return table->klass->get_rule (table, axis, x, y, color);
230 table_cell_format_footnote_markers (const struct table_cell *cell,
233 for (size_t i = 0; i < cell->n_footnotes; i++)
236 ds_put_byte (s, ',');
237 ds_put_cstr (s, cell->footnotes[i]->marker);
241 static const struct footnote **
242 add_footnotes (const struct footnote **refs, size_t n_refs,
243 const struct footnote **footnotes, size_t *allocated, size_t *n)
245 for (size_t i = 0; i < n_refs; i++)
247 const struct footnote *f = refs[i];
248 if (f->idx >= *allocated)
250 size_t new_allocated = (f->idx + 1) * 2;
251 footnotes = xrealloc (footnotes, new_allocated * sizeof *footnotes);
252 while (*allocated < new_allocated)
253 footnotes[(*allocated)++] = NULL;
255 footnotes[f->idx] = f;
263 table_collect_footnotes (const struct table_item *item,
264 const struct footnote ***footnotesp)
266 const struct footnote **footnotes = NULL;
267 size_t allocated = 0;
270 struct table *t = item->table;
271 for (int y = 0; y < table_nr (t); y++)
273 struct table_cell cell;
274 for (int x = 0; x < table_nc (t); x = cell.d[TABLE_HORZ][1])
276 table_get_cell (t, x, y, &cell);
278 if (x == cell.d[TABLE_HORZ][0] && y == cell.d[TABLE_VERT][0])
279 footnotes = add_footnotes (cell.footnotes, cell.n_footnotes,
280 footnotes, &allocated, &n);
281 table_cell_free (&cell);
285 const struct table_item_text *title = table_item_get_title (item);
287 footnotes = add_footnotes (title->footnotes, title->n_footnotes,
288 footnotes, &allocated, &n);
290 const struct table_item_text *caption = table_item_get_caption (item);
292 footnotes = add_footnotes (caption->footnotes, caption->n_footnotes,
293 footnotes, &allocated, &n);
295 *footnotesp = footnotes;
299 struct table_unshared
302 struct table *subtable;
305 static const struct table_class table_unshared_class;
307 /* Takes ownership of TABLE and returns a table with the same contents but
308 which is guaranteed not to be shared (as returned by table_is_shared()).
310 If TABLE is unshared, just returns TABLE.
312 The only real use for this function is to create a copy of TABLE in which
313 the headers can be adjusted, which is a pretty specialized use case. */
315 table_unshare (struct table *table)
317 if (!table_is_shared (table))
321 struct table_unshared *tiu = xmalloc (sizeof *tiu);
322 table_init (&tiu->table, &table_unshared_class);
323 table_set_nc (&tiu->table, table_nc (table));
324 table_set_nr (&tiu->table, table_nr (table));
325 table_set_hl (&tiu->table, table_hl (table));
326 table_set_hr (&tiu->table, table_hr (table));
327 table_set_ht (&tiu->table, table_ht (table));
328 table_set_hb (&tiu->table, table_hb (table));
329 tiu->subtable = table;
334 static struct table_unshared *
335 table_unshared_cast (const struct table *table)
337 assert (table->klass == &table_unshared_class);
338 return UP_CAST (table, struct table_unshared, table);
342 table_unshared_destroy (struct table *tiu_)
344 struct table_unshared *tiu = table_unshared_cast (tiu_);
345 table_unref (tiu->subtable);
350 table_unshared_get_cell (const struct table *tiu_, int x, int y,
351 struct table_cell *cell)
353 struct table_unshared *tiu = table_unshared_cast (tiu_);
354 table_get_cell (tiu->subtable, x, y, cell);
358 table_unshared_get_rule (const struct table *tiu_,
359 enum table_axis axis, int x, int y,
360 struct cell_color *color)
362 struct table_unshared *tiu = table_unshared_cast (tiu_);
363 return table_get_rule (tiu->subtable, axis, x, y, color);
366 static const struct table_class table_unshared_class =
368 table_unshared_destroy,
369 table_unshared_get_cell,
370 table_unshared_get_rule,
379 enum table_halign halign;
382 static const struct table_class table_string_class;
384 /* Returns a table that contains a single cell, whose contents are S with
385 options OPTIONS (a combination of TAB_* values). */
387 table_from_string (enum table_halign halign, const char *s)
389 struct table_string *ts = xmalloc (sizeof *ts);
390 table_init (&ts->table, &table_string_class);
391 ts->table.n[TABLE_HORZ] = ts->table.n[TABLE_VERT] = 1;
392 ts->string = xstrdup (s);
397 static struct table_string *
398 table_string_cast (const struct table *table)
400 assert (table->klass == &table_string_class);
401 return UP_CAST (table, struct table_string, table);
405 table_string_destroy (struct table *ts_)
407 struct table_string *ts = table_string_cast (ts_);
413 table_string_get_cell (const struct table *ts_, int x UNUSED, int y UNUSED,
414 struct table_cell *cell)
416 static const struct area_style styles[] = {
417 #define S(H) [H] = { AREA_STYLE_INITIALIZER__, .cell_style.halign = H }
418 S(TABLE_HALIGN_LEFT),
419 S(TABLE_HALIGN_CENTER),
420 S(TABLE_HALIGN_RIGHT),
421 S(TABLE_HALIGN_MIXED),
422 S(TABLE_HALIGN_DECIMAL),
424 struct table_string *ts = table_string_cast (ts_);
425 cell->d[TABLE_HORZ][0] = 0;
426 cell->d[TABLE_HORZ][1] = 1;
427 cell->d[TABLE_VERT][0] = 0;
428 cell->d[TABLE_VERT][1] = 1;
430 cell->style = &styles[table_halign_interpret (ts->halign, false)];
431 cell->text = ts->string;
432 cell->n_footnotes = 0;
433 cell->destructor = NULL;
438 table_string_get_rule (const struct table *ts UNUSED,
439 enum table_axis axis UNUSED, int x UNUSED, int y UNUSED,
440 struct cell_color *color UNUSED)
445 static const struct table_class table_string_class =
447 table_string_destroy,
448 table_string_get_cell,
449 table_string_get_rule,
455 table_halign_to_string (enum table_halign halign)
459 case TABLE_HALIGN_LEFT: return "left";
460 case TABLE_HALIGN_CENTER: return "center";
461 case TABLE_HALIGN_RIGHT: return "right";
462 case TABLE_HALIGN_DECIMAL: return "decimal";
463 case TABLE_HALIGN_MIXED: return "mixed";
464 default: return "**error**";
469 table_valign_to_string (enum table_valign valign)
473 case TABLE_VALIGN_TOP: return "top";
474 case TABLE_VALIGN_CENTER: return "center";
475 case TABLE_VALIGN_BOTTOM: return "bottom";
476 default: return "**error**";
481 table_halign_interpret (enum table_halign halign, bool numeric)
485 case TABLE_HALIGN_LEFT:
486 case TABLE_HALIGN_CENTER:
487 case TABLE_HALIGN_RIGHT:
490 case TABLE_HALIGN_MIXED:
491 return numeric ? TABLE_HALIGN_RIGHT : TABLE_HALIGN_LEFT;
493 case TABLE_HALIGN_DECIMAL:
494 return TABLE_HALIGN_DECIMAL;
502 font_style_copy (struct font_style *dst, const struct font_style *src)
506 dst->typeface = xstrdup (dst->typeface);
510 font_style_uninit (struct font_style *font)
513 free (font->typeface);
517 area_style_copy (struct area_style *dst, const struct area_style *src)
519 font_style_copy (&dst->font_style, &src->font_style);
520 dst->cell_style = src->cell_style;
524 area_style_uninit (struct area_style *area)
527 font_style_uninit (&area->font_style);
531 table_stroke_to_string (enum table_stroke stroke)
535 case TABLE_STROKE_NONE: return "none";
536 case TABLE_STROKE_SOLID: return "solid";
537 case TABLE_STROKE_DASHED: return "dashed";
538 case TABLE_STROKE_THICK: return "thick";
539 case TABLE_STROKE_THIN: return "thin";
540 case TABLE_STROKE_DOUBLE: return "double";
547 cell_color_dump (const struct cell_color *c)
550 printf ("rgba(%d, %d, %d, %d)", c->r, c->g, c->b, c->alpha);
552 printf ("#%02"PRIx8"%02"PRIx8"%02"PRIx8, c->r, c->g, c->b);
556 font_style_dump (const struct font_style *f)
558 printf ("%s %dpx ", f->typeface, f->size);
559 cell_color_dump (&f->fg[0]);
561 cell_color_dump (&f->bg[0]);
562 if (!cell_color_equal (&f->fg[0], &f->fg[1])
563 || !cell_color_equal (&f->bg[0], &f->bg[1]))
566 cell_color_dump (&f->fg[1]);
568 cell_color_dump (&f->bg[1]);
571 fputs (" bold", stdout);
573 fputs (" italic", stdout);
575 fputs (" underline", stdout);
579 cell_style_dump (const struct cell_style *c)
581 fputs (table_halign_to_string (c->halign), stdout);
582 if (c->halign == TABLE_HALIGN_DECIMAL)
583 printf ("(%.2gpx)", c->decimal_offset);
584 printf (" %s", table_valign_to_string (c->valign));
585 printf (" %d,%d,%d,%dpx",
586 c->margin[TABLE_HORZ][0], c->margin[TABLE_HORZ][1],
587 c->margin[TABLE_VERT][0], c->margin[TABLE_VERT][1]);