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)
142 int options = style_idx << TABLE_CELL_STYLE_SHIFT;
144 options |= TABLE_CELL_ROTATE;
146 table_put_owned (t, x1, y1, x2, y2, options,
147 pivot_value_new_user_text_nocopy (ds_steal_cstr (s)));
151 draw_line (struct table *t, enum pivot_border border_idx,
152 enum table_axis axis, int a, int b0, int b1)
155 table_hline (t, border_idx, b0, b1, a);
157 table_vline (t, border_idx, a, b0, b1);
160 /* Fills row or column headings into T.
162 This function uses terminology and variable names for column headings, but
163 it also applies to row headings because it uses variables for the
164 differences, e.g. when for column headings it would use the H axis, it
165 instead uses 'h', which is set to H for column headings and V for row
168 compose_headings (struct table *t,
169 const struct pivot_axis *h_axis, enum table_axis h,
170 const struct pivot_axis *v_axis,
171 enum pivot_border dim_col_horz,
172 enum pivot_border dim_col_vert,
173 enum pivot_border cat_col_horz,
174 enum pivot_border cat_col_vert,
175 const size_t *column_enumeration, size_t n_columns,
177 bool rotate_inner_labels, bool rotate_outer_labels,
178 int area[TABLE_N_AXES][2])
180 const enum table_axis v = !h;
181 const int v_size = h_axis->label_depth;
182 const int h_ofs = v_axis->label_depth + area[h][0];
184 if (!h_axis->n_dimensions || !n_columns || !v_size)
187 const int stride = MAX (1, h_axis->n_dimensions);
189 /* Below, we're going to iterate through the dimensions. Each dimension
190 occupies one or more rows in the heading. 'top_row' is the top row of
191 these (and 'top_row + d->label_depth - 1' is the bottom row). */
192 int top_row = area[v][0];
194 const int h_max = area[h][1];
195 const int v_max = area[v][1];
197 /* We're going to iterate through dimensions and the rows that label them
198 from top to bottom (from outer to inner dimensions). As we move downward,
199 we start drawing vertical rules to separate categories and groups. After
200 we start drawing a vertical rule in a particular horizontal position, it
201 continues until the bottom of the heading. vrules[pos] indicates whether,
202 in our current row, we have already started drawing a vertical rule in
203 horizontal position 'pos'. (There are n_columns + 1 horizontal positions.
204 We allocate all of them for convenience below but only the inner n_columns
205 - 1 of them really matter.)
207 Here's an example that shows how vertical rules continue all the way
210 +-----------------------------------------------------+ __
212 +-----------------+-----------------+-----------------+ |dimension "bbbb"
213 | bbbb1 | bbbb2 | bbbb3 | _|
214 +-----------------+-----------------+-----------------+ __
215 | aaaa | aaaa | aaaa | |
216 +-----+-----+-----+-----+-----+-----+-----+-----+-----+ |dimension "aaaa"
217 |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3| _|
218 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
223 |___________________vrules[] indexes__________________|
225 Our data structures are more naturally iterated from bottom to top (inner
226 to outer dimensions). A previous version of this code actually worked
227 like that, but it didn't draw all of the vertical lines correctly as shown
228 above. It ended up rendering the above heading much like shown below,
229 which isn't what users expect. The "aaaa" label really needs to be shown
230 three times for clarity:
232 +-----------------------------------------------------+
234 +-----------------+-----------------+-----------------+
235 | bbbb1 | bbbb2 | bbbb3 |
236 +-----------------+-----------------+-----------------+
238 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
239 |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|
240 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
242 bool *vrules = XCALLOC (n_columns + 1, bool);
243 vrules[0] = vrules[n_columns] = true;
244 for (int dim_index = h_axis->n_dimensions; --dim_index >= 0; )
246 const struct pivot_dimension *d = h_axis->dimensions[dim_index];
247 if (d->hide_all_labels)
250 for (int row_ofs = 0; row_ofs < d->label_depth; row_ofs++)
252 for (size_t x1 = 0; x1 < n_columns;)
254 const struct pivot_category *c = find_category (
255 d, dim_index, column_enumeration + x1 * stride,
256 d->label_depth - row_ofs - 1);
264 for (x2 = x1 + 1; x2 < n_columns; x2++)
268 const struct pivot_category *c2 = find_category (
269 d, dim_index, column_enumeration + x2 * stride,
270 d->label_depth - row_ofs - 1);
275 int y1 = top_row + row_ofs;
276 int y2 = top_row + row_ofs + c->extra_depth + 1;
277 if (pivot_category_is_leaf (c) || c->show_label)
279 int bb[TABLE_N_AXES][2];
280 bb[h][0] = x1 + h_ofs;
281 bb[h][1] = x2 + h_ofs - 1;
284 bool is_outer_row = y1 == area[v][0];
285 bool is_inner_row = y2 == v_size + area[v][0];
286 bool rotate = ((rotate_inner_labels && is_inner_row)
287 || (rotate_outer_labels && is_outer_row));
288 fill_cell_spanned (t, bb[H][0], bb[V][0], bb[H][1], bb[V][1],
289 label_style_idx, c->name, rotate, 0);
291 /* Draw all the vertical lines in our running example, other
292 than the far left and far right ones. Only the ones that
293 start in the last row of the heading are drawn with the
294 "category" style, the rest with the "dimension" style,
295 e.g. only the # below are category style:
297 +-----------------------------------------------------+
299 +-----------------+-----------------+-----------------+
300 | bbbb1 | bbbb2 | bbbb3 |
301 +-----------------+-----------------+-----------------+
302 | aaaa | aaaa | aaaa |
303 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
304 |aaaa1#aaaa2#aaaa3|aaaa1#aaaa2#aaaa3|aaaa1#aaaa2#aaaa3|
305 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
307 enum pivot_border style
308 = (y1 == v_size - 1 ? cat_col_vert : dim_col_vert);
311 draw_line (t, style, v, x2 + h_ofs, y1, v_max);
316 draw_line (t, style, v, x1 + h_ofs, y1, v_max);
321 /* Draws the horizontal lines within a dimension, that is, those
322 that separate a separating a category (or group) from its
323 parent group or dimension's label. Our running example
324 doesn't have groups but the ==== lines below show the
325 separators between categories and their dimension label:
327 +-----------------------------------------------------+
329 +=================+=================+=================+
330 | bbbb1 | bbbb2 | bbbb3 |
331 +-----------------+-----------------+-----------------+
332 | aaaa | aaaa | aaaa |
333 +=====+=====+=====+=====+=====+=====+=====+=====+=====+
334 |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|
335 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
337 if (c->parent && c->parent->show_label)
338 draw_line (t, cat_col_horz, h, y1, x1 + h_ofs, x2 + h_ofs - 1);
343 if (d->root->show_label_in_corner && h_ofs > area[h][0])
345 int bb[TABLE_N_AXES][2];
347 bb[h][1] = h_ofs - 1;
349 bb[v][1] = top_row + d->label_depth - 1;
350 fill_cell_spanned (t, bb[H][0], bb[V][0], bb[H][1], bb[V][1],
351 PIVOT_AREA_CORNER, d->root->name, false, 0);
354 /* Draw the horizontal line between dimensions, e.g. the ===== line here:
356 +-----------------------------------------------------+ __
358 +-----------------+-----------------+-----------------+ |dim "bbbb"
359 | bbbb1 | bbbb2 | bbbb3 | _|
360 +=================+=================+=================+ __
361 | aaaa | aaaa | aaaa | |
362 +-----+-----+-----+-----+-----+-----+-----+-----+-----+ |dim "aaaa"
363 |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3| _|
364 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
366 if (dim_index != h_axis->n_dimensions - 1)
367 draw_line (t, dim_col_horz, h, top_row, h_ofs, h_max);
368 top_row += d->label_depth;
373 static struct table *
374 create_aux_table (const struct pivot_table *pt, int nc, int nr,
377 struct table *table = table_create (nc, nr, 0, 0);
378 table->styles[style_idx] = table_area_style_override (
379 table->container, &pt->look->areas[style_idx], NULL, NULL, false);
385 add_references (const struct pivot_table *pt, const struct table *table,
386 bool *refs, size_t *n_refs)
391 for (int y = 0; y < table->n[V]; y++)
392 for (int x = 0; x < table->n[H]; )
394 struct table_cell cell;
395 table_get_cell (table, x, y, &cell);
397 if (x == cell.d[H][0] && y == cell.d[V][0])
399 const struct pivot_value_ex *ex = pivot_value_ex (cell.value);
400 for (size_t i = 0; i < ex->n_footnotes; i++)
402 size_t idx = ex->footnote_indexes[i];
403 assert (idx < pt->n_footnotes);
405 if (!refs[idx] && pt->footnotes[idx]->show)
413 x = cell.d[TABLE_HORZ][1];
417 static struct pivot_footnote **
418 collect_footnotes (const struct pivot_table *pt,
419 const struct table *title,
420 const struct table *layers,
421 const struct table *body,
422 const struct table *caption,
423 size_t *n_footnotesp)
425 if (!pt->n_footnotes)
431 bool *refs = XCALLOC (pt->n_footnotes, bool);
433 add_references (pt, title, refs, &n_refs);
434 add_references (pt, layers, refs, &n_refs);
435 add_references (pt, body, refs, &n_refs);
436 add_references (pt, caption, refs, &n_refs);
438 struct pivot_footnote **footnotes = xnmalloc (n_refs, sizeof *footnotes);
439 size_t n_footnotes = 0;
440 for (size_t i = 0; i < pt->n_footnotes; i++)
442 footnotes[n_footnotes++] = pt->footnotes[i];
443 assert (n_footnotes == n_refs);
447 *n_footnotesp = n_footnotes;
451 static enum pivot_border
452 pivot_border_fallback (enum pivot_border border)
456 case PIVOT_BORDER_TITLE:
457 case PIVOT_BORDER_OUTER_LEFT:
458 case PIVOT_BORDER_OUTER_TOP:
459 case PIVOT_BORDER_OUTER_RIGHT:
460 case PIVOT_BORDER_OUTER_BOTTOM:
461 case PIVOT_BORDER_INNER_LEFT:
462 case PIVOT_BORDER_INNER_TOP:
463 case PIVOT_BORDER_INNER_RIGHT:
464 case PIVOT_BORDER_INNER_BOTTOM:
465 case PIVOT_BORDER_DATA_LEFT:
466 case PIVOT_BORDER_DATA_TOP:
470 case PIVOT_BORDER_DIM_ROW_HORZ:
471 return PIVOT_BORDER_CAT_ROW_HORZ;
472 case PIVOT_BORDER_DIM_ROW_VERT:
473 return PIVOT_BORDER_CAT_ROW_VERT;
474 case PIVOT_BORDER_DIM_COL_HORZ:
475 return PIVOT_BORDER_CAT_COL_HORZ;
476 case PIVOT_BORDER_DIM_COL_VERT:
477 return PIVOT_BORDER_CAT_COL_VERT;
480 case PIVOT_BORDER_CAT_ROW_HORZ:
481 case PIVOT_BORDER_CAT_ROW_VERT:
482 case PIVOT_BORDER_CAT_COL_HORZ:
483 case PIVOT_BORDER_CAT_COL_VERT:
486 case PIVOT_N_BORDERS:
492 static struct table_border_style
493 resolve_border_style (const struct pivot_table_look *look, enum pivot_border b,
494 bool show_grid_lines)
496 struct table_border_style style = look->borders[b];
497 if (style.stroke != TABLE_STROKE_NONE)
500 style = look->borders[pivot_border_fallback (b)];
501 if (style.stroke != TABLE_STROKE_NONE)
505 return (struct table_border_style) { .stroke = TABLE_STROKE_DASHED,
506 .color = CELL_COLOR_BLACK };
511 static struct table *
512 pivot_output_title (const struct pivot_table *pt)
514 if (!pt->title || !pt->show_title)
516 struct table *title = create_aux_table (pt, 1, 1, PIVOT_AREA_TITLE);
517 fill_cell (title, 0, 0, PIVOT_AREA_TITLE, pt->title, false);
522 pivot_count_layers (const struct pivot_table *pt)
524 const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
526 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
528 const struct pivot_dimension *d = layer_axis->dimensions[i];
536 put_layers (struct table *t, const struct pivot_table *pt,
537 const size_t *layer_indexes, int x1, int y1 UNUSED, int x2, int y2)
539 assert (y1 + pivot_count_layers (pt) - 1 == y2);
540 const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
541 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
543 const struct pivot_dimension *d = layer_axis->dimensions[i];
547 struct string s = DS_EMPTY_INITIALIZER;
548 pivot_value_format (d->data_leaves[layer_indexes[i]]->name, pt, &s);
550 fill_cell_owned (t, x1, y, x2, y, PIVOT_AREA_LAYERS, &s, false);
554 static struct table *
555 pivot_output_layers (const struct pivot_table *pt, const size_t *layer_indexes)
557 int n_layers = pivot_count_layers (pt);
561 struct table *layers = create_aux_table (pt, 1, n_layers, PIVOT_AREA_LAYERS);
562 put_layers (layers, pt, layer_indexes, 0, 0, 0, n_layers - 1);
566 static struct table *
567 pivot_output_caption (const struct pivot_table *pt)
569 if (!pt->caption || !pt->show_caption)
572 struct table *caption = create_aux_table (pt, 1, 1, PIVOT_AREA_CAPTION);
573 fill_cell (caption, 0, 0, PIVOT_AREA_CAPTION, pt->caption, false);
578 put_footnotes (struct table *t, const struct pivot_table *pt,
579 struct pivot_footnote **f, size_t nf,
580 int x1, int x2, int y1)
582 for (size_t i = 0; i < nf; i++)
584 struct string s = DS_EMPTY_INITIALIZER;
585 pivot_footnote_format_marker (f[i], pt, &s);
586 ds_put_cstr (&s, ". ");
587 pivot_value_format (f[i]->content, pt, &s);
589 fill_cell_owned (t, x1, y, x2, y, PIVOT_AREA_FOOTER, &s, false);
593 static struct table *
594 pivot_output_footnotes (const struct pivot_table *pt,
595 struct pivot_footnote **f, size_t nf)
600 struct table *footnotes = create_aux_table (pt, 1, nf, PIVOT_AREA_FOOTER);
601 put_footnotes (footnotes, pt, f, nf, 0, 0, 0);
606 pivot_output (const struct pivot_table *pt,
607 const size_t *layer_indexes,
609 struct table **titlep,
610 struct table **layersp,
611 struct table **bodyp,
612 struct table **captionp,
613 struct table **footnotesp,
614 struct pivot_footnote ***fp, size_t *nfp)
616 const size_t *pindexes[PIVOT_N_AXES]
617 = { [PIVOT_AXIS_LAYER] = layer_indexes };
619 size_t data[TABLE_N_AXES];
620 size_t *column_enumeration = pivot_table_enumerate_axis (
621 pt, PIVOT_AXIS_COLUMN, layer_indexes, pt->look->omit_empty, &data[H]);
622 size_t *row_enumeration = pivot_table_enumerate_axis (
623 pt, PIVOT_AXIS_ROW, layer_indexes, pt->look->omit_empty, &data[V]);
625 int stub[TABLE_N_AXES] = {
626 [H] = pt->axes[PIVOT_AXIS_ROW].label_depth,
627 [V] = pt->axes[PIVOT_AXIS_COLUMN].label_depth,
630 struct table *body = table_create (stub[H] + data[H], stub[V] + data[V],
632 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
633 body->styles[i] = table_area_style_override (
634 body->container, &pt->look->areas[i], NULL, NULL, false);
636 body->n_borders = PIVOT_N_BORDERS;
637 body->borders = pool_nmalloc (body->container, PIVOT_N_BORDERS,
638 sizeof *body->borders);
639 for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
640 body->borders[i] = resolve_border_style (pt->look, i,
641 printing && pt->show_grid_lines);
643 int body_area[TABLE_N_AXES][2] = {
644 [H] = { 0, body->n[H] - 1 },
645 [V] = { 0, body->n[V] - 1 },
647 compose_headings (body,
648 &pt->axes[PIVOT_AXIS_COLUMN], H, &pt->axes[PIVOT_AXIS_ROW],
649 PIVOT_BORDER_DIM_COL_HORZ,
650 PIVOT_BORDER_DIM_COL_VERT,
651 PIVOT_BORDER_CAT_COL_HORZ,
652 PIVOT_BORDER_CAT_COL_VERT,
653 column_enumeration, data[H],
654 PIVOT_AREA_COLUMN_LABELS,
655 pt->rotate_outer_row_labels, false, body_area);
657 compose_headings (body,
658 &pt->axes[PIVOT_AXIS_ROW], V, &pt->axes[PIVOT_AXIS_COLUMN],
659 PIVOT_BORDER_DIM_ROW_VERT,
660 PIVOT_BORDER_DIM_ROW_HORZ,
661 PIVOT_BORDER_CAT_ROW_VERT,
662 PIVOT_BORDER_CAT_ROW_HORZ,
663 row_enumeration, data[V],
664 PIVOT_AREA_ROW_LABELS,
665 false, pt->rotate_inner_column_labels, body_area);
667 size_t *dindexes = XCALLOC (pt->n_dimensions, size_t);
669 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
670 &pt->axes[PIVOT_AXIS_ROW])
673 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
675 &pt->axes[PIVOT_AXIS_COLUMN])
677 pivot_table_convert_indexes_ptod (pt, pindexes, dindexes);
678 const struct pivot_value *value = pivot_table_get (pt, dindexes);
679 fill_cell (body, x + stub[H], y + stub[V],
680 PIVOT_AREA_DATA, value, false);
689 if ((pt->corner_text || !pt->look->row_labels_in_corner)
690 && stub[H] && stub[V])
691 fill_cell_spanned (body, 0, 0, stub[H] - 1, stub[V] - 1,
692 PIVOT_AREA_CORNER, pt->corner_text, false,
693 TABLE_CELL_FULL_WIDTH);
695 if (body->n[H] && body->n[V])
697 int inner[TABLE_N_AXES][2] = {
698 [H] = { 0, body->n[H] },
699 [V] = { 0, body->n[V] },
701 table_hline (body, PIVOT_BORDER_INNER_TOP,
702 inner[H][0], inner[H][1] - 1, inner[V][0]);
703 table_hline (body, PIVOT_BORDER_INNER_BOTTOM,
704 inner[H][0], inner[H][1] - 1, inner[V][1]);
705 table_vline (body, PIVOT_BORDER_INNER_LEFT,
706 inner[H][0], inner[V][0], inner[V][1] - 1);
707 table_vline (body, PIVOT_BORDER_INNER_LEFT,
708 inner[H][1], inner[V][0], inner[V][1] - 1);
711 table_hline (body, PIVOT_BORDER_DATA_TOP,
712 inner[H][0], inner[H][1] - 1, stub[V]);
714 table_vline (body, PIVOT_BORDER_DATA_LEFT,
715 stub[H], inner[V][0], inner[V][1] - 1);
718 free (column_enumeration);
719 free (row_enumeration);
723 *titlep = pivot_output_title (pt);
725 *layersp = pivot_output_layers (pt, layer_indexes);
727 *captionp = pivot_output_caption (pt);
729 if (fp || footnotesp)
732 struct pivot_footnote **f = collect_footnotes (
733 pt, titlep ? *titlep : NULL, layersp ? *layersp : NULL, body,
734 captionp ? *captionp : NULL, &nf);
737 *footnotesp = pivot_output_footnotes (pt, f, nf);
750 pivot_output_monolithic (const struct pivot_table *pt,
751 const size_t *layer_indexes,
756 #============ Frame ============#
759 # +--- Body ------------------+ #
761 # | +--------------------+ #
766 # +------+--------------------+ #
769 #===============================#
771 The regions are the following size:
773 - Frame: Either 0 or 1 row or column wide, depending on whether the table
774 style has an outer border on each side. The frame cells are always
775 empty and marked as TABLE_CELL_PADDING.
777 - Title: Either 0 or 1 row high.
779 - Layers: Usually, one row for each layer dimension, but less if a layer
780 dimension is empty (has no value).
782 - Body and data: Variable (and can be empty).
784 - Caption: Either 0 or 1 row high.
786 - Footnotes: From zero rows up to the number of footnotes in PT (only
787 footnotes referenced in the other regions are shown).
789 The title, layers, caption, and footnote Region join all the body's
790 columns into single cells.
793 /* Size of data region. */
794 size_t data[TABLE_N_AXES];
795 size_t *column_enumeration = pivot_table_enumerate_axis (
796 pt, PIVOT_AXIS_COLUMN, layer_indexes, pt->look->omit_empty, &data[H]);
797 size_t *row_enumeration = pivot_table_enumerate_axis (
798 pt, PIVOT_AXIS_ROW, layer_indexes, pt->look->omit_empty, &data[V]);
800 /* Size of stub region. */
801 int stub[TABLE_N_AXES] = {
802 [H] = pt->axes[PIVOT_AXIS_ROW].label_depth,
803 [V] = pt->axes[PIVOT_AXIS_COLUMN].label_depth,
806 int n_title = pt->title && pt->show_title;
807 int n_layers = pivot_count_layers (pt);
808 int n_caption = pt->caption && pt->show_caption;
809 int max_footnotes = pt->n_footnotes;
811 int min_body_width = n_title || n_layers || n_caption;
812 int n[TABLE_N_AXES] = {
813 [H] = MAX (min_body_width, stub[H] + data[H]),
814 [V] = n_title + n_layers + stub[V] + data[V] + n_caption + max_footnotes,
817 struct table *t = table_create (n[H], n[V],
818 stub[H], n_title + n_layers + stub[V]);
819 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
820 t->styles[i] = table_area_style_override (
821 t->container, &pt->look->areas[i], NULL, NULL, false);
823 t->n_borders = PIVOT_N_BORDERS;
824 t->borders = pool_nmalloc (t->container, PIVOT_N_BORDERS, sizeof *t->borders);
825 for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
827 const struct table_border_style *src = &pt->look->borders[i];
828 struct table_border_style *dst = &t->borders[i];
829 *dst = (!printing && pt->show_grid_lines && src->stroke == TABLE_STROKE_NONE
830 ? (struct table_border_style) { .stroke = TABLE_STROKE_DASHED,
831 .color = CELL_COLOR_BLACK }
835 int body[TABLE_N_AXES][2] = {
836 [H] = { 0, n[H] - 1 },
839 n_title + n_layers + stub[V] + data[V] - 1
844 put_layers (t, pt, layer_indexes,
846 body[H][1], n_title + n_layers - 1);
849 &pt->axes[PIVOT_AXIS_COLUMN], H, &pt->axes[PIVOT_AXIS_ROW],
850 PIVOT_BORDER_DIM_COL_HORZ,
851 PIVOT_BORDER_DIM_COL_VERT,
852 PIVOT_BORDER_CAT_COL_HORZ,
853 PIVOT_BORDER_CAT_COL_VERT,
854 column_enumeration, data[H],
855 PIVOT_AREA_COLUMN_LABELS,
856 pt->rotate_outer_row_labels, false, body);
859 &pt->axes[PIVOT_AXIS_ROW], V, &pt->axes[PIVOT_AXIS_COLUMN],
860 PIVOT_BORDER_DIM_ROW_VERT,
861 PIVOT_BORDER_DIM_ROW_HORZ,
862 PIVOT_BORDER_CAT_ROW_VERT,
863 PIVOT_BORDER_CAT_ROW_HORZ,
864 row_enumeration, data[V],
865 PIVOT_AREA_ROW_LABELS,
866 false, pt->rotate_inner_column_labels, body);
868 int data_ofs[TABLE_N_AXES] = {
869 [H] = body[H][0] + stub[H],
870 [V] = body[V][0] + stub[V],
873 const size_t *pindexes[PIVOT_N_AXES] = { [PIVOT_AXIS_LAYER] = layer_indexes };
874 size_t *dindexes = XCALLOC (pt->n_dimensions, size_t);
876 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
877 &pt->axes[PIVOT_AXIS_ROW])
880 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
882 &pt->axes[PIVOT_AXIS_COLUMN])
884 pivot_table_convert_indexes_ptod (pt, pindexes, dindexes);
885 const struct pivot_value *value = pivot_table_get (pt, dindexes);
886 fill_cell (t, x, y, PIVOT_AREA_DATA, value, false);
894 if ((pt->corner_text || !pt->look->row_labels_in_corner)
895 && stub[H] && stub[V])
896 fill_cell_spanned (t, body[H][0], body[V][0],
897 body[H][0] + stub[H] - 1, body[V][0] + stub[V] - 1,
898 PIVOT_AREA_CORNER, pt->corner_text, false,
899 TABLE_CELL_FULL_WIDTH);
901 if (body[H][1] >= body[H][0] && body[V][1] >= body[V][0])
903 table_hline (t, PIVOT_BORDER_INNER_TOP,
904 body[H][0], body[H][1], body[V][0]);
905 table_hline (t, PIVOT_BORDER_INNER_BOTTOM,
906 body[H][0], body[H][1], body[V][1] + 1);
907 table_vline (t, PIVOT_BORDER_INNER_LEFT,
908 body[H][0], body[V][0], body[V][1]);
909 table_vline (t, PIVOT_BORDER_INNER_RIGHT,
910 body[H][1] + 1, body[V][0], body[V][1]);
913 table_hline (t, PIVOT_BORDER_DATA_TOP,
914 body[H][0], body[H][1], data_ofs[V]);
916 table_vline (t, PIVOT_BORDER_DATA_LEFT,
917 data_ofs[H], body[V][0], body[V][1]);
921 fill_cell_spanned (t,
922 body[H][0], body[V][1] + 1,
923 body[H][1], body[V][1] + 1,
924 PIVOT_AREA_CAPTION, pt->caption, false,
925 TABLE_CELL_FULL_WIDTH);
928 struct pivot_footnote **f = collect_footnotes (pt, t, NULL, NULL, NULL, &nf);
929 assert (nf <= max_footnotes);
930 put_footnotes (t, pt, f, nf, body[H][0], body[H][1],
931 body[V][1] + 1 + n_caption);
932 t->n[V] = body[V][1] + 1 + n_caption + nf;
938 printf ("%d,%d - %d,%d\n",
942 fill_cell_spanned (t,
945 PIVOT_AREA_TITLE, pt->title, false,
946 TABLE_CELL_FULL_WIDTH);
949 free (column_enumeration);
950 free (row_enumeration);
953 printf ("output page:\n");
954 for (int y = 0; y < t->n[V]; y++)
955 for (int x = 0; x < t->n[H]; x++)
957 struct table_cell cell;
959 table_get_cell (t, x, y, &cell);
960 char *s = pivot_value_to_string (cell.value, NULL);
961 printf ("%d,%d - %d,%d: %s\n",
962 cell.d[H][0], cell.d[V][0],
963 cell.d[H][1], cell.d[V][1],
973 pivot_table_submit (struct pivot_table *pt)
975 output_item_submit (table_item_create (pt));