1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2018 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/>. */
21 #include "output/pivot-table.h"
23 #include "data/settings.h"
24 #include "libpspp/assertion.h"
25 #include "libpspp/pool.h"
26 #include "output/table.h"
27 #include "output/page-eject-item.h"
28 #include "output/table-item.h"
29 #include "output/text-item.h"
30 #include "output/table-provider.h"
32 #include "gl/minmax.h"
33 #include "gl/xalloc.h"
38 static const struct pivot_category *
39 find_category (const struct pivot_dimension *d, int dim_index,
40 const size_t *indexes, int row_ofs)
42 size_t index = indexes[dim_index];
43 assert (index < d->n_leaves);
44 for (const struct pivot_category *c = d->presentation_leaves[index];
47 /* A category can covert multiple rows. Only return the category for its
49 if (row_ofs == c->extra_depth)
52 row_ofs -= 1 + c->extra_depth;
59 static struct table_area_style *
60 table_area_style_override (struct pool *pool,
61 const struct table_area_style *in,
62 const struct cell_style *cell_,
63 const struct font_style *font_,
66 const struct cell_style *cell = cell_ ? cell_ : &in->cell_style;
67 const struct font_style *font = font_ ? font_ : &in->font_style;
69 struct table_area_style *out = (pool
70 ? pool_alloc (pool, sizeof *out)
71 : xmalloc (sizeof *out));
72 *out = (struct table_area_style) {
73 .cell_style.halign = rotate_label ? TABLE_HALIGN_CENTER : cell->halign,
74 .cell_style.valign = rotate_label ? TABLE_VALIGN_CENTER : cell->valign,
75 .cell_style.decimal_offset = cell->decimal_offset,
76 .cell_style.margin[H][0] = cell->margin[H][0],
77 .cell_style.margin[H][1] = cell->margin[H][1],
78 .cell_style.margin[V][0] = cell->margin[V][0],
79 .cell_style.margin[V][1] = cell->margin[V][1],
80 .font_style.fg[0] = font->fg[0],
81 .font_style.fg[1] = font->fg[1],
82 .font_style.bg[0] = font->bg[0],
83 .font_style.bg[1] = font->bg[1],
84 .font_style.typeface = (font->typeface
85 ? pool_strdup (pool, font->typeface)
87 .font_style.size = font->size,
88 .font_style.bold = font->bold,
89 .font_style.italic = font->italic,
90 .font_style.underline = font->underline,
91 .font_style.markup = font->markup,
97 format_cell (const struct pivot_value *value, int style_idx,
98 enum settings_value_show show_values,
99 enum settings_value_show show_variables,
100 bool rotate_label, struct string *s)
102 int options = style_idx << TAB_STYLE_SHIFT;
105 bool numeric = pivot_value_format_body (value, show_values,
108 options |= TAB_NUMERIC;
109 if (value->font_style && value->font_style->markup)
110 options |= TAB_MARKUP;
112 options |= TAB_ROTATE;
118 fill_cell (struct table *t, int x1, int y1, int x2, int y2,
119 const struct table_area_style *style, int style_idx,
120 const struct pivot_value *value, struct footnote **footnotes,
121 enum settings_value_show show_values,
122 enum settings_value_show show_variables,
125 struct string s = DS_EMPTY_INITIALIZER;
126 int options = format_cell (value, style_idx,
127 show_values, show_variables, rotate_label, &s);
128 table_joint_text (t, x1, y1, x2, y2, options, ds_cstr (&s));
133 if (value->cell_style || value->font_style || rotate_label)
134 table_add_style (t, x1, y1,
135 table_area_style_override (t->container, style,
140 for (size_t i = 0; i < value->n_footnotes; i++)
142 struct footnote *f = footnotes[value->footnotes[i]->idx];
144 table_add_footnote (t, x1, y1, f);
147 if (value->n_subscripts)
148 table_add_subscripts (t, x1, y1,
149 value->subscripts, value->n_subscripts);
153 static struct table_cell *
154 pivot_value_to_table_cell (const struct pivot_value *value,
155 const struct table_area_style *style, int style_idx,
156 struct footnote **footnotes,
157 enum settings_value_show show_values,
158 enum settings_value_show show_variables)
163 struct string s = DS_EMPTY_INITIALIZER;
164 int options = format_cell (value, style_idx,
165 show_values, show_variables, false, &s);
167 struct table_cell *cell = xmalloc (sizeof *cell);
168 *cell = (struct table_cell) {
170 .text = ds_steal_cstr (&s),
171 .style = table_area_style_override (
172 NULL, style, value->cell_style, value->font_style, false),
175 if (value->n_subscripts)
177 cell->subscripts = xnmalloc (value->n_subscripts,
178 sizeof *cell->subscripts);
179 cell->n_subscripts = value->n_subscripts;
180 for (size_t i = 0; i < value->n_subscripts; i++)
181 cell->subscripts[i] = xstrdup (value->subscripts[i]);
184 if (value->n_footnotes)
186 cell->footnotes = xnmalloc (value->n_footnotes, sizeof *cell->footnotes);
187 for (size_t i = 0; i < value->n_footnotes; i++)
189 struct footnote *f = footnotes[value->footnotes[i]->idx];
191 cell->footnotes[cell->n_footnotes++] = f;
199 get_table_rule (const struct table_border_style *styles,
200 enum pivot_border style_idx)
202 return styles[style_idx].stroke | (style_idx << TAB_RULE_STYLE_SHIFT);
206 draw_line (struct table *t, const struct table_border_style *styles,
207 enum pivot_border style_idx,
208 enum table_axis axis, int a, int b0, int b1)
210 int rule = get_table_rule (styles, style_idx);
212 table_hline (t, rule, b0, b1, a);
214 table_vline (t, rule, a, b0, b1);
217 /* Fills row or column headings into T.
219 This function uses terminology and variable names for column headings, but
220 it also applies to row headings because it uses variables for the
221 differences, e.g. when for column headings it would use the H axis, it
222 instead uses 'h', which is set to H for column headings and V for row
225 compose_headings (struct table *t,
226 const struct pivot_axis *a_axis, enum table_axis a,
227 const struct pivot_axis *b_axis,
228 const struct table_border_style *borders,
229 enum pivot_border dim_col_horz,
230 enum pivot_border dim_col_vert,
231 enum pivot_border cat_col_horz,
232 enum pivot_border cat_col_vert,
233 const size_t *column_enumeration, size_t n_columns,
234 const struct table_area_style *label_style,
236 const struct table_area_style *corner_style,
237 struct footnote **footnotes,
238 enum settings_value_show show_values,
239 enum settings_value_show show_variables,
240 bool rotate_inner_labels, bool rotate_outer_labels)
242 enum table_axis b = !a;
243 int b_size = a_axis->label_depth;
244 int a_ofs = b_axis->label_depth;
246 if (!a_axis->n_dimensions || !n_columns || !b_size)
249 const int stride = MAX (1, a_axis->n_dimensions);
251 /* Below, we're going to iterate through the dimensions. Each dimension
252 occupies one or more rows in the heading. 'top_row' is the top row of
253 these (and 'top_row + d->label_depth - 1' is the bottom row). */
256 /* We're going to iterate through dimensions and the rows that label them
257 from top to bottom (from outer to inner dimensions). As we move downward,
258 we start drawing vertical rules to separate categories and groups. After
259 we start drawing a vertical rule in a particular horizontal position, it
260 continues until the bottom of the heading. vrules[pos] indicates whether,
261 in our current row, we have already started drawing a vertical rule in
262 horizontal position 'pos'. (There are n_columns + 1 horizontal positions.
263 We allocate all of them for convenience below but only the inner n_columns
264 - 1 of them really matter.)
266 Here's an example that shows how vertical rules continue all the way
269 +-----------------------------------------------------+ __
271 +-----------------+-----------------+-----------------+ |dimension "bbbb"
272 | bbbb1 | bbbb2 | bbbb3 | _|
273 +-----------------+-----------------+-----------------+ __
274 | aaaa | aaaa | aaaa | |
275 +-----+-----+-----+-----+-----+-----+-----+-----+-----+ |dimension "aaaa"
276 |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3| _|
277 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
282 |___________________vrules[] indexes__________________|
284 Our data structures are more naturally iterated from bottom to top (inner
285 to outer dimensions). A previous version of this code actually worked
286 like that, but it didn't draw all of the vertical lines correctly as shown
287 above. It ended up rendering the above heading much like shown below,
288 which isn't what users expect. The "aaaa" label really needs to be shown
289 three times for clarity:
291 +-----------------------------------------------------+
293 +-----------------+-----------------+-----------------+
294 | bbbb1 | bbbb2 | bbbb3 |
295 +-----------------+-----------------+-----------------+
297 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
298 |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|
299 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
301 bool *vrules = xzalloc (n_columns + 1);
302 vrules[0] = vrules[n_columns] = true;
303 for (int dim_index = a_axis->n_dimensions; --dim_index >= 0; )
305 const struct pivot_dimension *d = a_axis->dimensions[dim_index];
306 if (d->hide_all_labels)
309 for (int row_ofs = 0; row_ofs < d->label_depth; row_ofs++)
311 for (size_t x1 = 0; x1 < n_columns;)
313 const struct pivot_category *c = find_category (
314 d, dim_index, column_enumeration + x1 * stride,
315 d->label_depth - row_ofs - 1);
323 for (x2 = x1 + 1; x2 < n_columns; x2++)
327 const struct pivot_category *c2 = find_category (
328 d, dim_index, column_enumeration + x2 * stride,
329 d->label_depth - row_ofs - 1);
334 int y1 = top_row + row_ofs;
335 int y2 = top_row + row_ofs + c->extra_depth + 1;
336 bool is_outer_row = y1 == 0;
337 bool is_inner_row = y2 == b_size;
338 if (pivot_category_is_leaf (c) || c->show_label)
340 int bb[TABLE_N_AXES][2];
341 bb[a][0] = x1 + a_ofs;
342 bb[a][1] = x2 + a_ofs - 1;
345 bool rotate = ((rotate_inner_labels && is_inner_row)
346 || (rotate_outer_labels && is_outer_row));
347 fill_cell (t, bb[H][0], bb[V][0], bb[H][1], bb[V][1],
348 label_style, label_style_idx, c->name, footnotes,
349 show_values, show_variables, rotate);
351 /* Draw all the vertical lines in our running example, other
352 than the far left and far right ones. Only the ones that
353 start in the last row of the heading are drawn with the
354 "category" style, the rest with the "dimension" style,
355 e.g. only the # below are category style:
357 +-----------------------------------------------------+
359 +-----------------+-----------------+-----------------+
360 | bbbb1 | bbbb2 | bbbb3 |
361 +-----------------+-----------------+-----------------+
362 | aaaa | aaaa | aaaa |
363 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
364 |aaaa1#aaaa2#aaaa3|aaaa1#aaaa2#aaaa3|aaaa1#aaaa2#aaaa3|
365 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
367 enum pivot_border style
368 = (y1 == b_size - 1 ? cat_col_vert : dim_col_vert);
371 draw_line (t, borders, style, b, x2 + a_ofs, y1,
377 draw_line (t, borders, style, b, x1 + a_ofs, y1,
383 /* Draws the horizontal lines within a dimension, that is, those
384 that separate a separating a category (or group) from its
385 parent group or dimension's label. Our running example
386 doesn't have groups but the ==== lines below show the
387 separators between categories and their dimension label:
389 +-----------------------------------------------------+
391 +=================+=================+=================+
392 | bbbb1 | bbbb2 | bbbb3 |
393 +-----------------+-----------------+-----------------+
394 | aaaa | aaaa | aaaa |
395 +=====+=====+=====+=====+=====+=====+=====+=====+=====+
396 |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|
397 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
399 if (c->parent && c->parent->show_label)
400 draw_line (t, borders, cat_col_horz, a, y1,
401 x1 + a_ofs, x2 + a_ofs - 1);
406 if (d->root->show_label_in_corner && a_ofs > 0)
408 int bb[TABLE_N_AXES][2];
410 bb[a][1] = a_ofs - 1;
412 bb[b][1] = top_row + d->label_depth - 1;
413 fill_cell (t, bb[H][0], bb[V][0], bb[H][1], bb[V][1],
414 corner_style, PIVOT_AREA_CORNER, d->root->name, footnotes,
415 show_values, show_variables, false);
418 /* Draw the horizontal line between dimensions, e.g. the ===== line here:
420 +-----------------------------------------------------+ __
422 +-----------------+-----------------+-----------------+ |dim "bbbb"
423 | bbbb1 | bbbb2 | bbbb3 | _|
424 +=================+=================+=================+ __
425 | aaaa | aaaa | aaaa | |
426 +-----+-----+-----+-----+-----+-----+-----+-----+-----+ |dim "aaaa"
427 |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3| _|
428 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
430 if (dim_index != a_axis->n_dimensions - 1)
431 draw_line (t, borders, dim_col_horz, a, top_row, a_ofs,
433 top_row += d->label_depth;
439 pivot_table_submit_layer (const struct pivot_table *pt,
440 const size_t *layer_indexes)
442 const size_t *pindexes[PIVOT_N_AXES]
443 = { [PIVOT_AXIS_LAYER] = layer_indexes };
445 size_t body[TABLE_N_AXES];
446 size_t *column_enumeration = pivot_table_enumerate_axis (
447 pt, PIVOT_AXIS_COLUMN, layer_indexes, pt->look->omit_empty, &body[H]);
448 size_t *row_enumeration = pivot_table_enumerate_axis (
449 pt, PIVOT_AXIS_ROW, layer_indexes, pt->look->omit_empty, &body[V]);
451 int stub[TABLE_N_AXES] = {
452 [H] = pt->axes[PIVOT_AXIS_ROW].label_depth,
453 [V] = pt->axes[PIVOT_AXIS_COLUMN].label_depth,
455 struct table *table = table_create (body[H] + stub[H],
457 stub[H], 0, stub[V], 0);
459 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
460 table->styles[i] = table_area_style_override (
461 table->container, &pt->look->areas[i], NULL, NULL, false);
463 for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
465 const struct table_border_style *in = &pt->look->borders[i];
466 table->rule_colors[i] = pool_alloc (table->container,
467 sizeof *table->rule_colors[i]);
468 struct cell_color *out = table->rule_colors[i];
469 out->alpha = in->color.alpha;
470 out->r = in->color.r;
471 out->g = in->color.g;
472 out->b = in->color.b;
475 struct footnote **footnotes = XCALLOC (pt->n_footnotes, struct footnote *);
476 for (size_t i = 0; i < pt->n_footnotes; i++)
478 const struct pivot_footnote *pf = pt->footnotes[i];
483 char *content = pivot_value_to_string (pf->content, pt->show_values,
485 char *marker = pivot_value_to_string (pf->marker, pt->show_values,
487 footnotes[i] = table_create_footnote (
488 table, i, content, marker,
489 table_area_style_override (table->container,
490 &pt->look->areas[PIVOT_AREA_FOOTER],
491 pf->content->cell_style,
492 pf->content->font_style,
498 compose_headings (table,
499 &pt->axes[PIVOT_AXIS_COLUMN], H, &pt->axes[PIVOT_AXIS_ROW],
501 PIVOT_BORDER_DIM_COL_HORZ,
502 PIVOT_BORDER_DIM_COL_VERT,
503 PIVOT_BORDER_CAT_COL_HORZ,
504 PIVOT_BORDER_CAT_COL_VERT,
505 column_enumeration, body[H],
506 &pt->look->areas[PIVOT_AREA_COLUMN_LABELS],
507 PIVOT_AREA_COLUMN_LABELS,
508 &pt->look->areas[PIVOT_AREA_CORNER], footnotes,
509 pt->show_values, pt->show_variables,
510 pt->rotate_outer_row_labels, false);
512 compose_headings (table,
513 &pt->axes[PIVOT_AXIS_ROW], V, &pt->axes[PIVOT_AXIS_COLUMN],
515 PIVOT_BORDER_DIM_ROW_VERT,
516 PIVOT_BORDER_DIM_ROW_HORZ,
517 PIVOT_BORDER_CAT_ROW_VERT,
518 PIVOT_BORDER_CAT_ROW_HORZ,
519 row_enumeration, body[V],
520 &pt->look->areas[PIVOT_AREA_ROW_LABELS],
521 PIVOT_AREA_ROW_LABELS,
522 &pt->look->areas[PIVOT_AREA_CORNER], footnotes,
523 pt->show_values, pt->show_variables,
524 false, pt->rotate_inner_column_labels);
526 size_t *dindexes = XCALLOC (pt->n_dimensions, size_t);
528 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
529 &pt->axes[PIVOT_AXIS_ROW])
532 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
534 &pt->axes[PIVOT_AXIS_COLUMN])
536 pivot_table_convert_indexes_ptod (pt, pindexes, dindexes);
537 const struct pivot_value *value = pivot_table_get (pt, dindexes);
539 x + stub[H], y + stub[V],
540 x + stub[H], y + stub[V],
541 &pt->look->areas[PIVOT_AREA_DATA], PIVOT_AREA_DATA,
543 pt->show_values, pt->show_variables, false);
552 if ((pt->corner_text || !pt->look->row_labels_in_corner)
553 && stub[H] && stub[V])
554 fill_cell (table, 0, 0, stub[H] - 1, stub[V] - 1,
555 &pt->look->areas[PIVOT_AREA_CORNER], PIVOT_AREA_CORNER,
556 pt->corner_text, footnotes,
557 pt->show_values, pt->show_variables, false);
559 if (table->n[H] && table->n[V])
562 table, get_table_rule (pt->look->borders, PIVOT_BORDER_INNER_TOP),
563 0, table->n[H] - 1, 0);
565 table, get_table_rule (pt->look->borders, PIVOT_BORDER_INNER_BOTTOM),
566 0, table->n[H] - 1, table->n[V]);
568 table, get_table_rule (pt->look->borders, PIVOT_BORDER_INNER_LEFT),
569 0, 0, table->n[V] - 1);
571 table, get_table_rule (pt->look->borders, PIVOT_BORDER_INNER_RIGHT),
572 table->n[H], 0, table->n[V] - 1);
576 table, get_table_rule (pt->look->borders, PIVOT_BORDER_DATA_TOP),
577 0, table->n[H] - 1, stub[V]);
580 table, get_table_rule (pt->look->borders, PIVOT_BORDER_DATA_LEFT),
581 stub[H], 0, table->n[V] - 1);
584 free (column_enumeration);
585 free (row_enumeration);
587 struct table_item *ti = table_item_create (table);
590 table_item_set_notes (ti, pt->notes);
592 if (pt->title && pt->show_title)
594 struct table_cell *title = pivot_value_to_table_cell (
595 pt->title, &pt->look->areas[PIVOT_AREA_TITLE], PIVOT_AREA_TITLE,
596 footnotes, pt->show_values, pt->show_variables);
597 table_item_set_title (ti, title);
598 table_cell_destroy (title);
601 const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
602 struct table_item_layers *layers = NULL;
603 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
605 const struct pivot_dimension *d = layer_axis->dimensions[i];
610 layers = xzalloc (sizeof *layers);
611 layers->style = table_area_style_override (
612 NULL, &pt->look->areas[PIVOT_AREA_LAYERS], NULL, NULL, false);
613 layers->layers = xnmalloc (layer_axis->n_dimensions,
614 sizeof *layers->layers);
617 const struct pivot_value *name
618 = d->data_leaves[layer_indexes[i]]->name;
619 struct table_item_layer *layer = &layers->layers[layers->n_layers++];
620 struct string s = DS_EMPTY_INITIALIZER;
621 pivot_value_format_body (name, pt->show_values, pt->show_variables,
623 layer->content = ds_steal_cstr (&s);
624 layer->n_footnotes = 0;
625 layer->footnotes = xnmalloc (name->n_footnotes,
626 sizeof *layer->footnotes);
627 for (size_t i = 0; i < name->n_footnotes; i++)
629 struct footnote *f = footnotes[name->footnotes[i]->idx];
631 layer->footnotes[layer->n_footnotes++] = f;
637 table_item_set_layers (ti, layers);
638 table_item_layers_destroy (layers);
641 if (pt->caption && pt->show_caption)
643 struct table_cell *caption = pivot_value_to_table_cell (
644 pt->caption, &pt->look->areas[PIVOT_AREA_CAPTION], PIVOT_AREA_CAPTION,
645 footnotes, pt->show_values, pt->show_variables);
646 table_item_set_caption (ti, caption);
647 table_cell_destroy (caption);
651 ti->pt = pivot_table_ref (pt);
653 table_item_submit (ti);
657 pivot_table_submit (struct pivot_table *pt)
659 pivot_table_assign_label_depth (CONST_CAST (struct pivot_table *, pt));
661 int old_decimal = settings_get_decimal_char (FMT_COMMA);
662 if (pt->decimal == '.' || pt->decimal == ',')
663 settings_set_decimal_char (pt->decimal);
665 if (pt->look->print_all_layers)
667 size_t *layer_indexes;
669 PIVOT_AXIS_FOR_EACH (layer_indexes, &pt->axes[PIVOT_AXIS_LAYER])
671 if (pt->look->paginate_layers)
672 page_eject_item_submit (page_eject_item_create ());
673 pivot_table_submit_layer (pt, layer_indexes);
677 pivot_table_submit_layer (pt, pt->current_layer);
679 settings_set_decimal_char (old_decimal);
681 pivot_table_unref (pt);