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-output.h"
23 #include "data/settings.h"
24 #include "libpspp/assertion.h"
25 #include "libpspp/pool.h"
26 #include "output/output-item.h"
27 #include "output/pivot-table.h"
28 #include "output/table-provider.h"
29 #include "output/table.h"
31 #include "gl/minmax.h"
32 #include "gl/xalloc.h"
38 pivot_output_next_layer (const struct pivot_table *pt, size_t *indexes,
41 const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
42 if (print && pt->look->print_all_layers)
43 return pivot_axis_iterator_next (indexes, layer_axis);
46 size_t size = layer_axis->n_dimensions * sizeof *pt->current_layer;
47 return size ? xmemdup (pt->current_layer, size) : xmalloc (1);
56 static const struct pivot_category *
57 find_category (const struct pivot_dimension *d, int dim_index,
58 const size_t *indexes, int row_ofs)
60 size_t index = indexes[dim_index];
61 assert (index < d->n_leaves);
62 for (const struct pivot_category *c = d->presentation_leaves[index];
65 /* A category can cover multiple rows. Only return the category for its
67 if (row_ofs == c->extra_depth)
70 row_ofs -= 1 + c->extra_depth;
77 static struct table_area_style *
78 table_area_style_override (struct pool *pool,
79 const struct table_area_style *in,
80 const struct cell_style *cell_,
81 const struct font_style *font_,
84 const struct cell_style *cell = cell_ ? cell_ : &in->cell_style;
85 const struct font_style *font = font_ ? font_ : &in->font_style;
87 struct table_area_style *out = (pool
88 ? pool_alloc (pool, sizeof *out)
89 : xmalloc (sizeof *out));
90 *out = (struct table_area_style) {
91 .cell_style.halign = rotate_label ? TABLE_HALIGN_CENTER : cell->halign,
92 .cell_style.valign = rotate_label ? TABLE_VALIGN_CENTER : cell->valign,
93 .cell_style.decimal_offset = cell->decimal_offset,
94 .cell_style.margin[H][0] = cell->margin[H][0],
95 .cell_style.margin[H][1] = cell->margin[H][1],
96 .cell_style.margin[V][0] = cell->margin[V][0],
97 .cell_style.margin[V][1] = cell->margin[V][1],
98 .font_style.fg[0] = font->fg[0],
99 .font_style.fg[1] = font->fg[1],
100 .font_style.bg[0] = font->bg[0],
101 .font_style.bg[1] = font->bg[1],
102 .font_style.typeface = (font->typeface
103 ? pool_strdup (pool, font->typeface)
105 .font_style.size = font->size,
106 .font_style.bold = font->bold,
107 .font_style.italic = font->italic,
108 .font_style.underline = font->underline,
109 .font_style.markup = font->markup,
115 fill_cell (struct table *t, int x, int y,
116 int style_idx, const struct pivot_value *value,
119 int options = style_idx << TABLE_CELL_STYLE_SHIFT;
121 options |= TABLE_CELL_ROTATE;
123 table_put (t, x, y, x, y, options, value);
127 fill_cell_spanned (struct table *t, int x1, int y1, int x2, int y2,
128 int style_idx, const struct pivot_value *value,
129 bool rotate_label, int options)
131 options |= style_idx << TABLE_CELL_STYLE_SHIFT;
133 options |= TABLE_CELL_ROTATE;
135 table_put (t, x1, y1, x2, y2, options, value);
139 fill_cell_owned (struct table *t, int x1, int y1, int x2, int y2,
140 int style_idx, struct string *s, bool rotate_label,
143 options |= style_idx << TABLE_CELL_STYLE_SHIFT;
145 options |= TABLE_CELL_ROTATE;
147 table_put_owned (t, x1, y1, x2, y2, options,
148 pivot_value_new_user_text_nocopy (ds_steal_cstr (s)));
152 draw_line (struct table *t, enum pivot_border border_idx,
153 enum table_axis axis, int a, int b0, int b1)
156 table_hline (t, border_idx, b0, b1, a);
158 table_vline (t, border_idx, a, b0, b1);
161 /* Fills row or column headings into T.
163 This function uses terminology and variable names for column headings, but
164 it also applies to row headings because it uses variables for the
165 differences, e.g. when for column headings it would use the H axis, it
166 instead uses 'h', which is set to H for column headings and V for row
169 compose_headings (struct table *t,
170 const struct pivot_axis *h_axis, enum table_axis h,
171 const struct pivot_axis *v_axis,
172 enum pivot_border dim_col_horz,
173 enum pivot_border dim_col_vert,
174 enum pivot_border cat_col_horz,
175 enum pivot_border cat_col_vert,
176 const size_t *column_enumeration, size_t n_columns,
178 bool rotate_inner_labels, bool rotate_outer_labels,
179 int area[TABLE_N_AXES][2])
181 const enum table_axis v = !h;
182 const int v_size = h_axis->label_depth;
183 const int h_ofs = v_axis->label_depth + area[h][0];
185 if (!h_axis->n_dimensions || !n_columns || !v_size)
188 const int stride = MAX (1, h_axis->n_dimensions);
190 /* Below, we're going to iterate through the dimensions. Each dimension
191 occupies one or more rows in the heading. 'top_row' is the top row of
192 these (and 'top_row + d->label_depth - 1' is the bottom row). */
193 int top_row = area[v][0];
195 const int h_max = area[h][1];
196 const int v_max = area[v][1];
198 /* We're going to iterate through dimensions and the rows that label them
199 from top to bottom (from outer to inner dimensions). As we move downward,
200 we start drawing vertical rules to separate categories and groups. After
201 we start drawing a vertical rule in a particular horizontal position, it
202 continues until the bottom of the heading. vrules[pos] indicates whether,
203 in our current row, we have already started drawing a vertical rule in
204 horizontal position 'pos'. (There are n_columns + 1 horizontal positions.
205 We allocate all of them for convenience below but only the inner n_columns
206 - 1 of them really matter.)
208 Here's an example that shows how vertical rules continue all the way
211 +-----------------------------------------------------+ __
213 +-----------------+-----------------+-----------------+ |dimension "bbbb"
214 | bbbb1 | bbbb2 | bbbb3 | _|
215 +-----------------+-----------------+-----------------+ __
216 | aaaa | aaaa | aaaa | |
217 +-----+-----+-----+-----+-----+-----+-----+-----+-----+ |dimension "aaaa"
218 |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3| _|
219 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
224 |___________________vrules[] indexes__________________|
226 Our data structures are more naturally iterated from bottom to top (inner
227 to outer dimensions). A previous version of this code actually worked
228 like that, but it didn't draw all of the vertical lines correctly as shown
229 above. It ended up rendering the above heading much like shown below,
230 which isn't what users expect. The "aaaa" label really needs to be shown
231 three times for clarity:
233 +-----------------------------------------------------+
235 +-----------------+-----------------+-----------------+
236 | bbbb1 | bbbb2 | bbbb3 |
237 +-----------------+-----------------+-----------------+
239 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
240 |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|
241 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
243 bool *vrules = XCALLOC (n_columns + 1, bool);
244 vrules[0] = vrules[n_columns] = true;
245 for (int dim_index = h_axis->n_dimensions; --dim_index >= 0; )
247 const struct pivot_dimension *d = h_axis->dimensions[dim_index];
248 if (d->hide_all_labels)
251 for (int row_ofs = 0; row_ofs < d->label_depth; row_ofs++)
253 for (size_t x1 = 0; x1 < n_columns;)
255 const struct pivot_category *c = find_category (
256 d, dim_index, column_enumeration + x1 * stride,
257 d->label_depth - row_ofs - 1);
265 for (x2 = x1 + 1; x2 < n_columns; x2++)
269 const struct pivot_category *c2 = find_category (
270 d, dim_index, column_enumeration + x2 * stride,
271 d->label_depth - row_ofs - 1);
276 int y1 = top_row + row_ofs;
277 int y2 = top_row + row_ofs + c->extra_depth + 1;
278 if (pivot_category_is_leaf (c) || c->show_label)
280 int bb[TABLE_N_AXES][2];
281 bb[h][0] = x1 + h_ofs;
282 bb[h][1] = x2 + h_ofs - 1;
285 bool is_outer_row = y1 == area[v][0];
286 bool is_inner_row = y2 == v_size + area[v][0];
287 bool rotate = ((rotate_inner_labels && is_inner_row)
288 || (rotate_outer_labels && is_outer_row));
289 fill_cell_spanned (t, bb[H][0], bb[V][0], bb[H][1], bb[V][1],
290 label_style_idx, c->name, rotate, 0);
292 /* Draw all the vertical lines in our running example, other
293 than the far left and far right ones. Only the ones that
294 start in the last row of the heading are drawn with the
295 "category" style, the rest with the "dimension" style,
296 e.g. only the # below are category style:
298 +-----------------------------------------------------+
300 +-----------------+-----------------+-----------------+
301 | bbbb1 | bbbb2 | bbbb3 |
302 +-----------------+-----------------+-----------------+
303 | aaaa | aaaa | aaaa |
304 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
305 |aaaa1#aaaa2#aaaa3|aaaa1#aaaa2#aaaa3|aaaa1#aaaa2#aaaa3|
306 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
308 enum pivot_border style
309 = (y1 == area[v][0] + v_size - 1 ? cat_col_vert : dim_col_vert);
312 draw_line (t, style, v, x2 + h_ofs, y1, v_max);
317 draw_line (t, style, v, x1 + h_ofs, y1, v_max);
322 /* Draws the horizontal lines within a dimension, that is, those
323 that separate a separating a category (or group) from its
324 parent group or dimension's label. Our running example
325 doesn't have groups but the ==== lines below show the
326 separators between categories and their dimension label:
328 +-----------------------------------------------------+
330 +=================+=================+=================+
331 | bbbb1 | bbbb2 | bbbb3 |
332 +-----------------+-----------------+-----------------+
333 | aaaa | aaaa | aaaa |
334 +=====+=====+=====+=====+=====+=====+=====+=====+=====+
335 |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|
336 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
338 if (c->parent && c->parent->show_label)
339 draw_line (t, cat_col_horz, h, y1, x1 + h_ofs, x2 + h_ofs - 1);
344 if (d->root->show_label_in_corner && h_ofs > area[h][0])
346 int bb[TABLE_N_AXES][2];
347 bb[h][0] = area[h][0];
348 bb[h][1] = h_ofs - 1;
350 bb[v][1] = top_row + d->label_depth - 1;
351 fill_cell_spanned (t, bb[H][0], bb[V][0], bb[H][1], bb[V][1],
352 PIVOT_AREA_CORNER, d->root->name, false, 0);
355 /* Draw the horizontal line between dimensions, e.g. the ===== line here:
357 +-----------------------------------------------------+ __
359 +-----------------+-----------------+-----------------+ |dim "bbbb"
360 | bbbb1 | bbbb2 | bbbb3 | _|
361 +=================+=================+=================+ __
362 | aaaa | aaaa | aaaa | |
363 +-----+-----+-----+-----+-----+-----+-----+-----+-----+ |dim "aaaa"
364 |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3| _|
365 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
367 if (dim_index != h_axis->n_dimensions - 1)
368 draw_line (t, dim_col_horz, h, top_row, h_ofs, h_max);
369 top_row += d->label_depth;
374 static struct table *
375 create_aux_table (const struct pivot_table *pt, int nc, int nr,
378 struct table *table = table_create (nc, nr, 0, 0);
379 table->styles[style_idx] = table_area_style_override (
380 table->container, &pt->look->areas[style_idx], NULL, NULL, false);
386 add_references (const struct pivot_table *pt, const struct table *table,
387 bool *refs, size_t *n_refs)
392 for (int y = 0; y < table->n[V]; y++)
393 for (int x = 0; x < table->n[H]; )
395 struct table_cell cell;
396 table_get_cell (table, x, y, &cell);
398 if (x == cell.d[H][0] && y == cell.d[V][0])
400 const struct pivot_value_ex *ex = pivot_value_ex (cell.value);
401 for (size_t i = 0; i < ex->n_footnotes; i++)
403 size_t idx = ex->footnote_indexes[i];
404 assert (idx < pt->n_footnotes);
406 if (!refs[idx] && pt->footnotes[idx]->show)
414 x = cell.d[TABLE_HORZ][1];
418 static struct pivot_footnote **
419 collect_footnotes (const struct pivot_table *pt,
420 const struct table *title,
421 const struct table *layers,
422 const struct table *body,
423 const struct table *caption,
424 size_t *n_footnotesp)
426 if (!pt->n_footnotes)
432 bool *refs = XCALLOC (pt->n_footnotes, bool);
434 add_references (pt, title, refs, &n_refs);
435 add_references (pt, layers, refs, &n_refs);
436 add_references (pt, body, refs, &n_refs);
437 add_references (pt, caption, refs, &n_refs);
439 struct pivot_footnote **footnotes = xnmalloc (n_refs, sizeof *footnotes);
440 size_t n_footnotes = 0;
441 for (size_t i = 0; i < pt->n_footnotes; i++)
443 footnotes[n_footnotes++] = pt->footnotes[i];
444 assert (n_footnotes == n_refs);
448 *n_footnotesp = n_footnotes;
452 static enum pivot_border
453 pivot_border_fallback (enum pivot_border border)
457 case PIVOT_BORDER_TITLE:
458 case PIVOT_BORDER_OUTER_LEFT:
459 case PIVOT_BORDER_OUTER_TOP:
460 case PIVOT_BORDER_OUTER_RIGHT:
461 case PIVOT_BORDER_OUTER_BOTTOM:
462 case PIVOT_BORDER_INNER_LEFT:
463 case PIVOT_BORDER_INNER_TOP:
464 case PIVOT_BORDER_INNER_RIGHT:
465 case PIVOT_BORDER_INNER_BOTTOM:
466 case PIVOT_BORDER_DATA_LEFT:
467 case PIVOT_BORDER_DATA_TOP:
471 case PIVOT_BORDER_DIM_ROW_HORZ:
472 return PIVOT_BORDER_CAT_ROW_HORZ;
473 case PIVOT_BORDER_DIM_ROW_VERT:
474 return PIVOT_BORDER_CAT_ROW_VERT;
475 case PIVOT_BORDER_DIM_COL_HORZ:
476 return PIVOT_BORDER_CAT_COL_HORZ;
477 case PIVOT_BORDER_DIM_COL_VERT:
478 return PIVOT_BORDER_CAT_COL_VERT;
481 case PIVOT_BORDER_CAT_ROW_HORZ:
482 case PIVOT_BORDER_CAT_ROW_VERT:
483 case PIVOT_BORDER_CAT_COL_HORZ:
484 case PIVOT_BORDER_CAT_COL_VERT:
487 case PIVOT_N_BORDERS:
493 static struct table_border_style
494 resolve_border_style (const struct pivot_table_look *look, enum pivot_border b,
495 bool show_grid_lines)
497 struct table_border_style style = look->borders[b];
498 if (style.stroke != TABLE_STROKE_NONE)
501 style = look->borders[pivot_border_fallback (b)];
502 if (style.stroke != TABLE_STROKE_NONE)
506 return (struct table_border_style) { .stroke = TABLE_STROKE_DASHED,
507 .color = CELL_COLOR_BLACK };
512 static struct table *
513 pivot_output_title (const struct pivot_table *pt)
515 if (!pt->title || !pt->show_title)
517 struct table *title = create_aux_table (pt, 1, 1, PIVOT_AREA_TITLE);
518 fill_cell (title, 0, 0, PIVOT_AREA_TITLE, pt->title, false);
523 pivot_count_layers (const struct pivot_table *pt)
525 const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
527 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
529 const struct pivot_dimension *d = layer_axis->dimensions[i];
537 put_layers (struct table *t, const struct pivot_table *pt,
538 const size_t *layer_indexes, int x1, int y1 UNUSED, int x2, int y2)
540 assert (y1 + pivot_count_layers (pt) - 1 == y2);
541 const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
542 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
544 const struct pivot_dimension *d = layer_axis->dimensions[i];
548 struct string s = DS_EMPTY_INITIALIZER;
549 pivot_value_format (d->data_leaves[layer_indexes[i]]->name, pt, &s);
551 fill_cell_owned (t, x1, y, x2, y, PIVOT_AREA_LAYERS, &s, false,
552 TABLE_CELL_FULL_WIDTH);
556 static struct table *
557 pivot_output_layers (const struct pivot_table *pt, const size_t *layer_indexes)
559 int n_layers = pivot_count_layers (pt);
563 struct table *layers = create_aux_table (pt, 1, n_layers, PIVOT_AREA_LAYERS);
564 put_layers (layers, pt, layer_indexes, 0, 0, 0, n_layers - 1);
568 static struct table *
569 pivot_output_caption (const struct pivot_table *pt)
571 if (!pt->caption || !pt->show_caption)
574 struct table *caption = create_aux_table (pt, 1, 1, PIVOT_AREA_CAPTION);
575 fill_cell (caption, 0, 0, PIVOT_AREA_CAPTION, pt->caption, false);
580 put_footnotes (struct table *t, const struct pivot_table *pt,
581 struct pivot_footnote **f, size_t nf,
582 int x1, int x2, int y1)
584 for (size_t i = 0; i < nf; i++)
586 struct string s = DS_EMPTY_INITIALIZER;
587 pivot_footnote_format_marker (f[i], pt, &s);
588 ds_put_cstr (&s, ". ");
589 pivot_value_format (f[i]->content, pt, &s);
591 fill_cell_owned (t, x1, y, x2, y, PIVOT_AREA_FOOTER, &s, false,
592 TABLE_CELL_FULL_WIDTH);
596 static struct table *
597 pivot_output_footnotes (const struct pivot_table *pt,
598 struct pivot_footnote **f, size_t nf)
603 struct table *footnotes = create_aux_table (pt, 1, nf, PIVOT_AREA_FOOTER);
604 put_footnotes (footnotes, pt, f, nf, 0, 0, 0);
609 pivot_output (const struct pivot_table *pt,
610 const size_t *layer_indexes,
612 struct table **titlep,
613 struct table **layersp,
614 struct table **bodyp,
615 struct table **captionp,
616 struct table **footnotesp,
617 struct pivot_footnote ***fp, size_t *nfp)
619 const size_t *pindexes[PIVOT_N_AXES]
620 = { [PIVOT_AXIS_LAYER] = layer_indexes };
622 size_t data[TABLE_N_AXES];
623 size_t *column_enumeration = pivot_table_enumerate_axis (
624 pt, PIVOT_AXIS_COLUMN, layer_indexes, pt->look->omit_empty, &data[H]);
625 size_t *row_enumeration = pivot_table_enumerate_axis (
626 pt, PIVOT_AXIS_ROW, layer_indexes, pt->look->omit_empty, &data[V]);
628 int stub[TABLE_N_AXES] = {
629 [H] = pt->axes[PIVOT_AXIS_ROW].label_depth,
630 [V] = pt->axes[PIVOT_AXIS_COLUMN].label_depth,
633 struct table *body = table_create (stub[H] + data[H], stub[V] + data[V],
635 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
636 body->styles[i] = table_area_style_override (
637 body->container, &pt->look->areas[i], NULL, NULL, false);
639 body->n_borders = PIVOT_N_BORDERS;
640 body->borders = pool_nmalloc (body->container, PIVOT_N_BORDERS,
641 sizeof *body->borders);
642 for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
643 body->borders[i] = resolve_border_style (pt->look, i,
644 printing && pt->show_grid_lines);
646 int body_area[TABLE_N_AXES][2] = {
647 [H] = { 0, body->n[H] - 1 },
648 [V] = { 0, body->n[V] - 1 },
650 compose_headings (body,
651 &pt->axes[PIVOT_AXIS_COLUMN], H, &pt->axes[PIVOT_AXIS_ROW],
652 PIVOT_BORDER_DIM_COL_HORZ,
653 PIVOT_BORDER_DIM_COL_VERT,
654 PIVOT_BORDER_CAT_COL_HORZ,
655 PIVOT_BORDER_CAT_COL_VERT,
656 column_enumeration, data[H],
657 PIVOT_AREA_COLUMN_LABELS,
658 pt->rotate_outer_row_labels, false, body_area);
660 compose_headings (body,
661 &pt->axes[PIVOT_AXIS_ROW], V, &pt->axes[PIVOT_AXIS_COLUMN],
662 PIVOT_BORDER_DIM_ROW_VERT,
663 PIVOT_BORDER_DIM_ROW_HORZ,
664 PIVOT_BORDER_CAT_ROW_VERT,
665 PIVOT_BORDER_CAT_ROW_HORZ,
666 row_enumeration, data[V],
667 PIVOT_AREA_ROW_LABELS,
668 false, pt->rotate_inner_column_labels, body_area);
670 size_t *dindexes = XCALLOC (pt->n_dimensions, size_t);
672 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
673 &pt->axes[PIVOT_AXIS_ROW])
676 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
678 &pt->axes[PIVOT_AXIS_COLUMN])
680 pivot_table_convert_indexes_ptod (pt, pindexes, dindexes);
681 const struct pivot_value *value = pivot_table_get (pt, dindexes);
682 fill_cell (body, x + stub[H], y + stub[V],
683 PIVOT_AREA_DATA, value, false);
692 if ((pt->corner_text || !pt->look->row_labels_in_corner)
693 && stub[H] && stub[V])
694 fill_cell_spanned (body, 0, 0, stub[H] - 1, stub[V] - 1,
695 PIVOT_AREA_CORNER, pt->corner_text, false,
696 TABLE_CELL_FULL_WIDTH);
698 if (body->n[H] && body->n[V])
700 int inner[TABLE_N_AXES][2] = {
701 [H] = { 0, body->n[H] },
702 [V] = { 0, body->n[V] },
704 table_hline (body, PIVOT_BORDER_INNER_TOP,
705 inner[H][0], inner[H][1] - 1, inner[V][0]);
706 table_hline (body, PIVOT_BORDER_INNER_BOTTOM,
707 inner[H][0], inner[H][1] - 1, inner[V][1]);
708 table_vline (body, PIVOT_BORDER_INNER_LEFT,
709 inner[H][0], inner[V][0], inner[V][1] - 1);
710 table_vline (body, PIVOT_BORDER_INNER_LEFT,
711 inner[H][1], inner[V][0], inner[V][1] - 1);
714 table_hline (body, PIVOT_BORDER_DATA_TOP,
715 inner[H][0], inner[H][1] - 1, stub[V]);
717 table_vline (body, PIVOT_BORDER_DATA_LEFT,
718 stub[H], inner[V][0], inner[V][1] - 1);
721 free (column_enumeration);
722 free (row_enumeration);
726 *titlep = pivot_output_title (pt);
728 *layersp = pivot_output_layers (pt, layer_indexes);
730 *captionp = pivot_output_caption (pt);
732 if (fp || footnotesp)
735 struct pivot_footnote **f = collect_footnotes (
736 pt, titlep ? *titlep : NULL, layersp ? *layersp : NULL, body,
737 captionp ? *captionp : NULL, &nf);
740 *footnotesp = pivot_output_footnotes (pt, f, nf);
753 pivot_output_monolithic (const struct pivot_table *pt,
754 const size_t *layer_indexes,
759 #============ Frame ============#
762 # +--- Body ------------------+ #
764 # | +--------------------+ #
769 # +------+--------------------+ #
772 #===============================#
774 The regions are the following size:
776 - Frame: Either 0 or 1 row or column wide, depending on whether the table
777 style has an outer border on each side. The frame cells are always
778 empty and marked as TABLE_CELL_PADDING.
780 - Title: Either 0 or 1 row high.
782 - Layers: Usually, one row for each layer dimension, but less if a layer
783 dimension is empty (has no value).
785 - Body and data: Variable (and can be empty).
787 - Caption: Either 0 or 1 row high.
789 - Footnotes: From zero rows up to the number of footnotes in PT (only
790 footnotes referenced in the other regions are shown).
792 The title, layers, caption, and footnote Region join all the body's
793 columns into single cells.
796 /* Size of data region. */
797 size_t data[TABLE_N_AXES];
798 size_t *column_enumeration = pivot_table_enumerate_axis (
799 pt, PIVOT_AXIS_COLUMN, layer_indexes, pt->look->omit_empty, &data[H]);
800 size_t *row_enumeration = pivot_table_enumerate_axis (
801 pt, PIVOT_AXIS_ROW, layer_indexes, pt->look->omit_empty, &data[V]);
803 /* Size of stub region. */
804 int stub[TABLE_N_AXES] = {
805 [H] = pt->axes[PIVOT_AXIS_ROW].label_depth,
806 [V] = pt->axes[PIVOT_AXIS_COLUMN].label_depth,
809 int n_title = pt->title && pt->show_title;
810 int n_layers = pivot_count_layers (pt);
811 int n_caption = pt->caption && pt->show_caption;
812 int max_footnotes = pt->n_footnotes;
814 int min_body_width = n_title || n_layers || n_caption;
815 int n[TABLE_N_AXES] = {
816 [H] = MAX (min_body_width, stub[H] + data[H]),
817 [V] = n_title + n_layers + stub[V] + data[V] + n_caption + max_footnotes,
820 struct table *t = table_create (n[H], n[V],
821 stub[H], n_title + n_layers + stub[V]);
822 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
823 t->styles[i] = table_area_style_override (
824 t->container, &pt->look->areas[i], NULL, NULL, false);
826 t->n_borders = PIVOT_N_BORDERS;
827 t->borders = pool_nmalloc (t->container, PIVOT_N_BORDERS, sizeof *t->borders);
828 for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
829 t->borders[i] = resolve_border_style (pt->look, i,
830 printing && pt->show_grid_lines);
832 int body[TABLE_N_AXES][2] = {
833 [H] = { 0, n[H] - 1 },
836 n_title + n_layers + stub[V] + data[V] - 1
841 put_layers (t, pt, layer_indexes,
843 body[H][1], n_title + n_layers - 1);
846 &pt->axes[PIVOT_AXIS_COLUMN], H, &pt->axes[PIVOT_AXIS_ROW],
847 PIVOT_BORDER_DIM_COL_HORZ,
848 PIVOT_BORDER_DIM_COL_VERT,
849 PIVOT_BORDER_CAT_COL_HORZ,
850 PIVOT_BORDER_CAT_COL_VERT,
851 column_enumeration, data[H],
852 PIVOT_AREA_COLUMN_LABELS,
853 pt->rotate_outer_row_labels, false, body);
856 &pt->axes[PIVOT_AXIS_ROW], V, &pt->axes[PIVOT_AXIS_COLUMN],
857 PIVOT_BORDER_DIM_ROW_VERT,
858 PIVOT_BORDER_DIM_ROW_HORZ,
859 PIVOT_BORDER_CAT_ROW_VERT,
860 PIVOT_BORDER_CAT_ROW_HORZ,
861 row_enumeration, data[V],
862 PIVOT_AREA_ROW_LABELS,
863 false, pt->rotate_inner_column_labels, body);
865 int data_ofs[TABLE_N_AXES] = {
866 [H] = body[H][0] + stub[H],
867 [V] = body[V][0] + stub[V],
870 const size_t *pindexes[PIVOT_N_AXES] = { [PIVOT_AXIS_LAYER] = layer_indexes };
871 size_t *dindexes = XCALLOC (pt->n_dimensions, size_t);
873 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
874 &pt->axes[PIVOT_AXIS_ROW])
877 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
879 &pt->axes[PIVOT_AXIS_COLUMN])
881 pivot_table_convert_indexes_ptod (pt, pindexes, dindexes);
882 const struct pivot_value *value = pivot_table_get (pt, dindexes);
883 fill_cell (t, x, y, PIVOT_AREA_DATA, value, false);
891 if ((pt->corner_text || !pt->look->row_labels_in_corner)
892 && stub[H] && stub[V])
893 fill_cell_spanned (t, body[H][0], body[V][0],
894 body[H][0] + stub[H] - 1, body[V][0] + stub[V] - 1,
895 PIVOT_AREA_CORNER, pt->corner_text, false, 0);
897 if (body[H][1] >= body[H][0] && body[V][1] >= body[V][0])
899 table_hline (t, PIVOT_BORDER_INNER_TOP,
900 body[H][0], body[H][1], body[V][0]);
901 table_hline (t, PIVOT_BORDER_INNER_BOTTOM,
902 body[H][0], body[H][1], body[V][1] + 1);
903 table_vline (t, PIVOT_BORDER_INNER_LEFT,
904 body[H][0], body[V][0], body[V][1]);
905 table_vline (t, PIVOT_BORDER_INNER_RIGHT,
906 body[H][1] + 1, body[V][0], body[V][1]);
909 table_hline (t, PIVOT_BORDER_DATA_TOP,
910 body[H][0], body[H][1], data_ofs[V]);
912 table_vline (t, PIVOT_BORDER_DATA_LEFT,
913 data_ofs[H], body[V][0], body[V][1]);
917 fill_cell_spanned (t,
918 body[H][0], body[V][1] + 1,
919 body[H][1], body[V][1] + 1,
920 PIVOT_AREA_CAPTION, pt->caption, false,
921 TABLE_CELL_FULL_WIDTH);
924 struct pivot_footnote **f = collect_footnotes (pt, t, NULL, NULL, NULL, &nf);
925 assert (nf <= max_footnotes);
926 put_footnotes (t, pt, f, nf, body[H][0], body[H][1],
927 body[V][1] + 1 + n_caption);
928 t->n[V] = body[V][1] + 1 + n_caption + nf;
934 printf ("%d,%d - %d,%d\n",
938 fill_cell_spanned (t,
941 PIVOT_AREA_TITLE, pt->title, false,
942 TABLE_CELL_FULL_WIDTH);
945 free (column_enumeration);
946 free (row_enumeration);
949 printf ("output page:\n");
950 for (int y = 0; y < t->n[V]; y++)
951 for (int x = 0; x < t->n[H]; x++)
953 struct table_cell cell;
955 table_get_cell (t, x, y, &cell);
956 char *s = pivot_value_to_string (cell.value, NULL);
957 printf ("%d,%d - %d,%d: %s\n",
958 cell.d[H][0], cell.d[V][0],
959 cell.d[H][1], cell.d[V][1],
969 pivot_table_submit (struct pivot_table *pt)
971 output_item_submit (table_item_create (pt));