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;
198 static struct table_item_text *
199 pivot_value_to_table_item_text (const struct pivot_value *value,
200 const struct table_area_style *area,
201 struct footnote **footnotes,
202 enum settings_value_show show_values,
203 enum settings_value_show show_variables)
208 struct string s = DS_EMPTY_INITIALIZER;
209 pivot_value_format_body (value, show_values, show_variables, &s);
211 struct table_item_text *text = xmalloc (sizeof *text);
212 *text = (struct table_item_text) {
213 .content = ds_steal_cstr (&s),
214 .footnotes = xnmalloc (value->n_footnotes, sizeof *text->footnotes),
215 .style = table_area_style_override (
216 NULL, area, value->cell_style, value->font_style, false),
219 for (size_t i = 0; i < value->n_footnotes; i++)
221 struct footnote *f = footnotes[value->footnotes[i]->idx];
223 text->footnotes[text->n_footnotes++] = f;
230 get_table_rule (const struct table_border_style *styles,
231 enum pivot_border style_idx)
233 return styles[style_idx].stroke | (style_idx << TAB_RULE_STYLE_SHIFT);
237 draw_line (struct table *t, const struct table_border_style *styles,
238 enum pivot_border style_idx,
239 enum table_axis axis, int a, int b0, int b1)
241 int rule = get_table_rule (styles, style_idx);
243 table_hline (t, rule, b0, b1, a);
245 table_vline (t, rule, a, b0, b1);
248 /* Fills row or column headings into T.
250 This function uses terminology and variable names for column headings, but
251 it also applies to row headings because it uses variables for the
252 differences, e.g. when for column headings it would use the H axis, it
253 instead uses 'h', which is set to H for column headings and V for row
256 compose_headings (struct table *t,
257 const struct pivot_axis *a_axis, enum table_axis a,
258 const struct pivot_axis *b_axis,
259 const struct table_border_style *borders,
260 enum pivot_border dim_col_horz,
261 enum pivot_border dim_col_vert,
262 enum pivot_border cat_col_horz,
263 enum pivot_border cat_col_vert,
264 const size_t *column_enumeration, size_t n_columns,
265 const struct table_area_style *label_style,
267 const struct table_area_style *corner_style,
268 struct footnote **footnotes,
269 enum settings_value_show show_values,
270 enum settings_value_show show_variables,
271 bool rotate_inner_labels, bool rotate_outer_labels)
273 enum table_axis b = !a;
274 int b_size = a_axis->label_depth;
275 int a_ofs = b_axis->label_depth;
277 if (!a_axis->n_dimensions || !n_columns || !b_size)
280 const int stride = MAX (1, a_axis->n_dimensions);
282 /* Below, we're going to iterate through the dimensions. Each dimension
283 occupies one or more rows in the heading. 'top_row' is the top row of
284 these (and 'top_row + d->label_depth - 1' is the bottom row). */
287 /* We're going to iterate through dimensions and the rows that label them
288 from top to bottom (from outer to inner dimensions). As we move downward,
289 we start drawing vertical rules to separate categories and groups. After
290 we start drawing a vertical rule in a particular horizontal position, it
291 continues until the bottom of the heading. vrules[pos] indicates whether,
292 in our current row, we have already started drawing a vertical rule in
293 horizontal position 'pos'. (There are n_columns + 1 horizontal positions.
294 We allocate all of them for convenience below but only the inner n_columns
295 - 1 of them really matter.)
297 Here's an example that shows how vertical rules continue all the way
300 +-----------------------------------------------------+ __
302 +-----------------+-----------------+-----------------+ |dimension "bbbb"
303 | bbbb1 | bbbb2 | bbbb3 | _|
304 +-----------------+-----------------+-----------------+ __
305 | aaaa | aaaa | aaaa | |
306 +-----+-----+-----+-----+-----+-----+-----+-----+-----+ |dimension "aaaa"
307 |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3| _|
308 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
313 |___________________vrules[] indexes__________________|
315 Our data structures are more naturally iterated from bottom to top (inner
316 to outer dimensions). A previous version of this code actually worked
317 like that, but it didn't draw all of the vertical lines correctly as shown
318 above. It ended up rendering the above heading much like shown below,
319 which isn't what users expect. The "aaaa" label really needs to be shown
320 three times for clarity:
322 +-----------------------------------------------------+
324 +-----------------+-----------------+-----------------+
325 | bbbb1 | bbbb2 | bbbb3 |
326 +-----------------+-----------------+-----------------+
328 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
329 |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|
330 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
332 bool *vrules = xzalloc (n_columns + 1);
333 vrules[0] = vrules[n_columns] = true;
334 for (int dim_index = a_axis->n_dimensions; --dim_index >= 0; )
336 const struct pivot_dimension *d = a_axis->dimensions[dim_index];
337 if (d->hide_all_labels)
340 for (int row_ofs = 0; row_ofs < d->label_depth; row_ofs++)
342 for (size_t x1 = 0; x1 < n_columns;)
344 const struct pivot_category *c = find_category (
345 d, dim_index, column_enumeration + x1 * stride,
346 d->label_depth - row_ofs - 1);
354 for (x2 = x1 + 1; x2 < n_columns; x2++)
358 const struct pivot_category *c2 = find_category (
359 d, dim_index, column_enumeration + x2 * stride,
360 d->label_depth - row_ofs - 1);
365 int y1 = top_row + row_ofs;
366 int y2 = top_row + row_ofs + c->extra_depth + 1;
367 bool is_outer_row = y1 == 0;
368 bool is_inner_row = y2 == b_size;
369 if (pivot_category_is_leaf (c) || c->show_label)
371 int bb[TABLE_N_AXES][2];
372 bb[a][0] = x1 + a_ofs;
373 bb[a][1] = x2 + a_ofs - 1;
376 bool rotate = ((rotate_inner_labels && is_inner_row)
377 || (rotate_outer_labels && is_outer_row));
378 fill_cell (t, bb[H][0], bb[V][0], bb[H][1], bb[V][1],
379 label_style, label_style_idx, c->name, footnotes,
380 show_values, show_variables, rotate);
382 /* Draw all the vertical lines in our running example, other
383 than the far left and far right ones. Only the ones that
384 start in the last row of the heading are drawn with the
385 "category" style, the rest with the "dimension" style,
386 e.g. only the # below are category style:
388 +-----------------------------------------------------+
390 +-----------------+-----------------+-----------------+
391 | bbbb1 | bbbb2 | bbbb3 |
392 +-----------------+-----------------+-----------------+
393 | aaaa | aaaa | aaaa |
394 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
395 |aaaa1#aaaa2#aaaa3|aaaa1#aaaa2#aaaa3|aaaa1#aaaa2#aaaa3|
396 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
398 enum pivot_border style
399 = (y1 == b_size - 1 ? cat_col_vert : dim_col_vert);
402 draw_line (t, borders, style, b, x2 + a_ofs, y1,
408 draw_line (t, borders, style, b, x1 + a_ofs, y1,
414 /* Draws the horizontal lines within a dimension, that is, those
415 that separate a separating a category (or group) from its
416 parent group or dimension's label. Our running example
417 doesn't have groups but the ==== lines below show the
418 separators between categories and their dimension label:
420 +-----------------------------------------------------+
422 +=================+=================+=================+
423 | bbbb1 | bbbb2 | bbbb3 |
424 +-----------------+-----------------+-----------------+
425 | aaaa | aaaa | aaaa |
426 +=====+=====+=====+=====+=====+=====+=====+=====+=====+
427 |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|
428 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
430 if (c->parent && c->parent->show_label)
431 draw_line (t, borders, cat_col_horz, a, y1,
432 x1 + a_ofs, x2 + a_ofs - 1);
437 if (d->root->show_label_in_corner && a_ofs > 0)
439 int bb[TABLE_N_AXES][2];
441 bb[a][1] = a_ofs - 1;
443 bb[b][1] = top_row + d->label_depth - 1;
444 fill_cell (t, bb[H][0], bb[V][0], bb[H][1], bb[V][1],
445 corner_style, PIVOT_AREA_CORNER, d->root->name, footnotes,
446 show_values, show_variables, false);
449 /* Draw the horizontal line between dimensions, e.g. the ===== line here:
451 +-----------------------------------------------------+ __
453 +-----------------+-----------------+-----------------+ |dim "bbbb"
454 | bbbb1 | bbbb2 | bbbb3 | _|
455 +=================+=================+=================+ __
456 | aaaa | aaaa | aaaa | |
457 +-----+-----+-----+-----+-----+-----+-----+-----+-----+ |dim "aaaa"
458 |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3| _|
459 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
461 if (dim_index != a_axis->n_dimensions - 1)
462 draw_line (t, borders, dim_col_horz, a, top_row, a_ofs,
464 top_row += d->label_depth;
470 pivot_table_submit_layer (const struct pivot_table *pt,
471 const size_t *layer_indexes)
473 const size_t *pindexes[PIVOT_N_AXES]
474 = { [PIVOT_AXIS_LAYER] = layer_indexes };
476 size_t body[TABLE_N_AXES];
477 size_t *column_enumeration = pivot_table_enumerate_axis (
478 pt, PIVOT_AXIS_COLUMN, layer_indexes, pt->look->omit_empty, &body[H]);
479 size_t *row_enumeration = pivot_table_enumerate_axis (
480 pt, PIVOT_AXIS_ROW, layer_indexes, pt->look->omit_empty, &body[V]);
482 int stub[TABLE_N_AXES] = {
483 [H] = pt->axes[PIVOT_AXIS_ROW].label_depth,
484 [V] = pt->axes[PIVOT_AXIS_COLUMN].label_depth,
486 struct table *table = table_create (body[H] + stub[H],
488 stub[H], 0, stub[V], 0);
490 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
491 table->styles[i] = table_area_style_override (
492 table->container, &pt->look->areas[i], NULL, NULL, false);
494 for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
496 const struct table_border_style *in = &pt->look->borders[i];
497 table->rule_colors[i] = pool_alloc (table->container,
498 sizeof *table->rule_colors[i]);
499 struct cell_color *out = table->rule_colors[i];
500 out->alpha = in->color.alpha;
501 out->r = in->color.r;
502 out->g = in->color.g;
503 out->b = in->color.b;
506 struct footnote **footnotes = XCALLOC (pt->n_footnotes, struct footnote *);
507 for (size_t i = 0; i < pt->n_footnotes; i++)
509 const struct pivot_footnote *pf = pt->footnotes[i];
514 char *content = pivot_value_to_string (pf->content, pt->show_values,
516 char *marker = pivot_value_to_string (pf->marker, pt->show_values,
518 footnotes[i] = table_create_footnote (
519 table, i, content, marker,
520 table_area_style_override (table->container,
521 &pt->look->areas[PIVOT_AREA_FOOTER],
522 pf->content->cell_style,
523 pf->content->font_style,
529 compose_headings (table,
530 &pt->axes[PIVOT_AXIS_COLUMN], H, &pt->axes[PIVOT_AXIS_ROW],
532 PIVOT_BORDER_DIM_COL_HORZ,
533 PIVOT_BORDER_DIM_COL_VERT,
534 PIVOT_BORDER_CAT_COL_HORZ,
535 PIVOT_BORDER_CAT_COL_VERT,
536 column_enumeration, body[H],
537 &pt->look->areas[PIVOT_AREA_COLUMN_LABELS],
538 PIVOT_AREA_COLUMN_LABELS,
539 &pt->look->areas[PIVOT_AREA_CORNER], footnotes,
540 pt->show_values, pt->show_variables,
541 pt->rotate_outer_row_labels, false);
543 compose_headings (table,
544 &pt->axes[PIVOT_AXIS_ROW], V, &pt->axes[PIVOT_AXIS_COLUMN],
546 PIVOT_BORDER_DIM_ROW_VERT,
547 PIVOT_BORDER_DIM_ROW_HORZ,
548 PIVOT_BORDER_CAT_ROW_VERT,
549 PIVOT_BORDER_CAT_ROW_HORZ,
550 row_enumeration, body[V],
551 &pt->look->areas[PIVOT_AREA_ROW_LABELS],
552 PIVOT_AREA_ROW_LABELS,
553 &pt->look->areas[PIVOT_AREA_CORNER], footnotes,
554 pt->show_values, pt->show_variables,
555 false, pt->rotate_inner_column_labels);
557 size_t *dindexes = XCALLOC (pt->n_dimensions, size_t);
559 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
560 &pt->axes[PIVOT_AXIS_ROW])
563 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
565 &pt->axes[PIVOT_AXIS_COLUMN])
567 pivot_table_convert_indexes_ptod (pt, pindexes, dindexes);
568 const struct pivot_value *value = pivot_table_get (pt, dindexes);
570 x + stub[H], y + stub[V],
571 x + stub[H], y + stub[V],
572 &pt->look->areas[PIVOT_AREA_DATA], PIVOT_AREA_DATA,
574 pt->show_values, pt->show_variables, false);
583 if ((pt->corner_text || !pt->look->row_labels_in_corner)
584 && stub[H] && stub[V])
585 fill_cell (table, 0, 0, stub[H] - 1, stub[V] - 1,
586 &pt->look->areas[PIVOT_AREA_CORNER], PIVOT_AREA_CORNER,
587 pt->corner_text, footnotes,
588 pt->show_values, pt->show_variables, false);
590 if (table->n[H] && table->n[V])
593 table, get_table_rule (pt->look->borders, PIVOT_BORDER_INNER_TOP),
594 0, table->n[H] - 1, 0);
596 table, get_table_rule (pt->look->borders, PIVOT_BORDER_INNER_BOTTOM),
597 0, table->n[H] - 1, table->n[V]);
599 table, get_table_rule (pt->look->borders, PIVOT_BORDER_INNER_LEFT),
600 0, 0, table->n[V] - 1);
602 table, get_table_rule (pt->look->borders, PIVOT_BORDER_INNER_RIGHT),
603 table->n[H], 0, table->n[V] - 1);
607 table, get_table_rule (pt->look->borders, PIVOT_BORDER_DATA_TOP),
608 0, table->n[H] - 1, stub[V]);
611 table, get_table_rule (pt->look->borders, PIVOT_BORDER_DATA_LEFT),
612 stub[H], 0, table->n[V] - 1);
615 free (column_enumeration);
616 free (row_enumeration);
618 struct table_item *ti = table_item_create (table);
621 table_item_set_notes (ti, pt->notes);
623 if (pt->title && pt->show_title)
625 struct table_cell *title = pivot_value_to_table_cell (
626 pt->title, &pt->look->areas[PIVOT_AREA_TITLE], PIVOT_AREA_TITLE,
627 footnotes, pt->show_values, pt->show_variables);
628 table_item_set_title (ti, title);
629 table_cell_destroy (title);
632 const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
633 struct table_item_layers *layers = NULL;
634 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
636 const struct pivot_dimension *d = layer_axis->dimensions[i];
641 layers = xzalloc (sizeof *layers);
642 layers->style = table_area_style_override (
643 NULL, &pt->look->areas[PIVOT_AREA_LAYERS], NULL, NULL, false);
644 layers->layers = xnmalloc (layer_axis->n_dimensions,
645 sizeof *layers->layers);
648 const struct pivot_value *name
649 = d->data_leaves[layer_indexes[i]]->name;
650 struct table_item_layer *layer = &layers->layers[layers->n_layers++];
651 struct string s = DS_EMPTY_INITIALIZER;
652 pivot_value_format_body (name, pt->show_values, pt->show_variables,
654 layer->content = ds_steal_cstr (&s);
655 layer->n_footnotes = 0;
656 layer->footnotes = xnmalloc (name->n_footnotes,
657 sizeof *layer->footnotes);
658 for (size_t i = 0; i < name->n_footnotes; i++)
660 struct footnote *f = footnotes[name->footnotes[i]->idx];
662 layer->footnotes[layer->n_footnotes++] = f;
668 table_item_set_layers (ti, layers);
669 table_item_layers_destroy (layers);
672 if (pt->caption && pt->show_caption)
674 struct table_item_text *caption = pivot_value_to_table_item_text (
675 pt->caption, &pt->look->areas[PIVOT_AREA_CAPTION], footnotes,
676 pt->show_values, pt->show_variables);
677 table_item_set_caption (ti, caption);
678 table_item_text_destroy (caption);
682 ti->pt = pivot_table_ref (pt);
684 table_item_submit (ti);
688 pivot_table_submit (struct pivot_table *pt)
690 pivot_table_assign_label_depth (CONST_CAST (struct pivot_table *, pt));
692 int old_decimal = settings_get_decimal_char (FMT_COMMA);
693 if (pt->decimal == '.' || pt->decimal == ',')
694 settings_set_decimal_char (pt->decimal);
696 if (pt->look->print_all_layers)
698 size_t *layer_indexes;
700 PIVOT_AXIS_FOR_EACH (layer_indexes, &pt->axes[PIVOT_AXIS_LAYER])
702 if (pt->look->paginate_layers)
703 page_eject_item_submit (page_eject_item_create ());
704 pivot_table_submit_layer (pt, layer_indexes);
708 pivot_table_submit_layer (pt, pt->current_layer);
710 settings_set_decimal_char (old_decimal);
712 pivot_table_unref (pt);