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 == 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 struct table *
453 pivot_output_title (const struct pivot_table *pt)
455 if (!pt->title || !pt->show_title)
457 struct table *title = create_aux_table (pt, 1, 1, PIVOT_AREA_TITLE);
458 fill_cell (title, 0, 0, PIVOT_AREA_TITLE, pt->title, false);
463 pivot_count_layers (const struct pivot_table *pt)
465 const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
467 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
469 const struct pivot_dimension *d = layer_axis->dimensions[i];
477 put_layers (struct table *t, const struct pivot_table *pt,
478 const size_t *layer_indexes, int x1, int y1 UNUSED, int x2, int y2)
480 assert (y1 + pivot_count_layers (pt) - 1 == y2);
481 const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
482 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
484 const struct pivot_dimension *d = layer_axis->dimensions[i];
488 struct string s = DS_EMPTY_INITIALIZER;
489 pivot_value_format (d->data_leaves[layer_indexes[i]]->name, pt, &s);
491 fill_cell_owned (t, x1, y, x2, y, PIVOT_AREA_LAYERS, &s, false,
492 TABLE_CELL_FULL_WIDTH);
496 static struct table *
497 pivot_output_layers (const struct pivot_table *pt, const size_t *layer_indexes)
499 int n_layers = pivot_count_layers (pt);
503 struct table *layers = create_aux_table (pt, 1, n_layers, PIVOT_AREA_LAYERS);
504 put_layers (layers, pt, layer_indexes, 0, 0, 0, n_layers - 1);
508 static struct table *
509 pivot_output_caption (const struct pivot_table *pt)
511 if (!pt->caption || !pt->show_caption)
514 struct table *caption = create_aux_table (pt, 1, 1, PIVOT_AREA_CAPTION);
515 fill_cell (caption, 0, 0, PIVOT_AREA_CAPTION, pt->caption, false);
520 put_footnotes (struct table *t, const struct pivot_table *pt,
521 struct pivot_footnote **f, size_t nf,
522 int x1, int x2, int y1)
524 for (size_t i = 0; i < nf; i++)
526 struct string s = DS_EMPTY_INITIALIZER;
527 pivot_footnote_format_marker (f[i], pt, &s);
528 ds_put_cstr (&s, ". ");
529 pivot_value_format (f[i]->content, pt, &s);
531 fill_cell_owned (t, x1, y, x2, y, PIVOT_AREA_FOOTER, &s, false,
532 TABLE_CELL_FULL_WIDTH);
536 static struct table *
537 pivot_output_footnotes (const struct pivot_table *pt,
538 struct pivot_footnote **f, size_t nf)
543 struct table *footnotes = create_aux_table (pt, 1, nf, PIVOT_AREA_FOOTER);
544 put_footnotes (footnotes, pt, f, nf, 0, 0, 0);
549 pivot_output (const struct pivot_table *pt,
550 const size_t *layer_indexes,
552 struct table **titlep,
553 struct table **layersp,
554 struct table **bodyp,
555 struct table **captionp,
556 struct table **footnotesp,
557 struct pivot_footnote ***fp, size_t *nfp)
559 const size_t *pindexes[PIVOT_N_AXES]
560 = { [PIVOT_AXIS_LAYER] = layer_indexes };
562 size_t data[TABLE_N_AXES];
563 size_t *column_enumeration = pivot_table_enumerate_axis (
564 pt, PIVOT_AXIS_COLUMN, layer_indexes, pt->look->omit_empty, &data[H]);
565 size_t *row_enumeration = pivot_table_enumerate_axis (
566 pt, PIVOT_AXIS_ROW, layer_indexes, pt->look->omit_empty, &data[V]);
568 int stub[TABLE_N_AXES] = {
569 [H] = pt->axes[PIVOT_AXIS_ROW].label_depth,
570 [V] = pt->axes[PIVOT_AXIS_COLUMN].label_depth,
573 struct table *body = table_create (stub[H] + data[H], stub[V] + data[V],
575 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
576 body->styles[i] = table_area_style_override (
577 body->container, &pt->look->areas[i], NULL, NULL, false);
579 body->n_borders = PIVOT_N_BORDERS;
580 body->borders = pool_nmalloc (body->container, PIVOT_N_BORDERS,
581 sizeof *body->borders);
582 for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
584 const struct table_border_style *src = &pt->look->borders[i];
585 struct table_border_style *dst = &body->borders[i];
586 *dst = (!printing && pt->show_grid_lines && src->stroke == TABLE_STROKE_NONE
587 ? (struct table_border_style) { .stroke = TABLE_STROKE_DASHED,
588 .color = CELL_COLOR_BLACK }
592 int body_area[TABLE_N_AXES][2] = {
593 [H] = { 0, body->n[H] - 1 },
594 [V] = { 0, body->n[V] - 1 },
596 compose_headings (body,
597 &pt->axes[PIVOT_AXIS_COLUMN], H, &pt->axes[PIVOT_AXIS_ROW],
598 PIVOT_BORDER_DIM_COL_HORZ,
599 PIVOT_BORDER_DIM_COL_VERT,
600 PIVOT_BORDER_CAT_COL_HORZ,
601 PIVOT_BORDER_CAT_COL_VERT,
602 column_enumeration, data[H],
603 PIVOT_AREA_COLUMN_LABELS,
604 pt->rotate_outer_row_labels, false, body_area);
606 compose_headings (body,
607 &pt->axes[PIVOT_AXIS_ROW], V, &pt->axes[PIVOT_AXIS_COLUMN],
608 PIVOT_BORDER_DIM_ROW_VERT,
609 PIVOT_BORDER_DIM_ROW_HORZ,
610 PIVOT_BORDER_CAT_ROW_VERT,
611 PIVOT_BORDER_CAT_ROW_HORZ,
612 row_enumeration, data[V],
613 PIVOT_AREA_ROW_LABELS,
614 false, pt->rotate_inner_column_labels, body_area);
616 size_t *dindexes = XCALLOC (pt->n_dimensions, size_t);
618 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
619 &pt->axes[PIVOT_AXIS_ROW])
622 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
624 &pt->axes[PIVOT_AXIS_COLUMN])
626 pivot_table_convert_indexes_ptod (pt, pindexes, dindexes);
627 const struct pivot_value *value = pivot_table_get (pt, dindexes);
628 fill_cell (body, x + stub[H], y + stub[V],
629 PIVOT_AREA_DATA, value, false);
638 if ((pt->corner_text || !pt->look->row_labels_in_corner)
639 && stub[H] && stub[V])
640 fill_cell_spanned (body, 0, 0, stub[H] - 1, stub[V] - 1,
641 PIVOT_AREA_CORNER, pt->corner_text, false,
642 TABLE_CELL_FULL_WIDTH);
644 if (body->n[H] && body->n[V])
646 int inner[TABLE_N_AXES][2] = {
647 [H] = { 0, body->n[H] },
648 [V] = { 0, body->n[V] },
650 table_hline (body, PIVOT_BORDER_INNER_TOP,
651 inner[H][0], inner[H][1] - 1, inner[V][0]);
652 table_hline (body, PIVOT_BORDER_INNER_BOTTOM,
653 inner[H][0], inner[H][1] - 1, inner[V][1]);
654 table_vline (body, PIVOT_BORDER_INNER_LEFT,
655 inner[H][0], inner[V][0], inner[V][1] - 1);
656 table_vline (body, PIVOT_BORDER_INNER_LEFT,
657 inner[H][1], inner[V][0], inner[V][1] - 1);
660 table_hline (body, PIVOT_BORDER_DATA_TOP,
661 inner[H][0], inner[H][1] - 1, stub[V]);
663 table_vline (body, PIVOT_BORDER_DATA_LEFT,
664 stub[H], inner[V][0], inner[V][1] - 1);
667 free (column_enumeration);
668 free (row_enumeration);
672 *titlep = pivot_output_title (pt);
674 *layersp = pivot_output_layers (pt, layer_indexes);
676 *captionp = pivot_output_caption (pt);
678 if (fp || footnotesp)
681 struct pivot_footnote **f = collect_footnotes (
682 pt, titlep ? *titlep : NULL, layersp ? *layersp : NULL, body,
683 captionp ? *captionp : NULL, &nf);
686 *footnotesp = pivot_output_footnotes (pt, f, nf);
699 pivot_output_monolithic (const struct pivot_table *pt,
700 const size_t *layer_indexes,
705 #============ Frame ============#
708 # +--- Body ------------------+ #
710 # | +--------------------+ #
715 # +------+--------------------+ #
718 #===============================#
720 The regions are the following size:
722 - Frame: Either 0 or 1 row or column wide, depending on whether the table
723 style has an outer border on each side. The frame cells are always
724 empty and marked as TABLE_CELL_PADDING.
726 - Title: Either 0 or 1 row high.
728 - Layers: Usually, one row for each layer dimension, but less if a layer
729 dimension is empty (has no value).
731 - Body and data: Variable (and can be empty).
733 - Caption: Either 0 or 1 row high.
735 - Footnotes: From zero rows up to the number of footnotes in PT (only
736 footnotes referenced in the other regions are shown).
738 The title, layers, caption, and footnote Region join all the body's
739 columns into single cells.
742 /* Size of data region. */
743 size_t data[TABLE_N_AXES];
744 size_t *column_enumeration = pivot_table_enumerate_axis (
745 pt, PIVOT_AXIS_COLUMN, layer_indexes, pt->look->omit_empty, &data[H]);
746 size_t *row_enumeration = pivot_table_enumerate_axis (
747 pt, PIVOT_AXIS_ROW, layer_indexes, pt->look->omit_empty, &data[V]);
749 /* Size of stub region. */
750 int stub[TABLE_N_AXES] = {
751 [H] = pt->axes[PIVOT_AXIS_ROW].label_depth,
752 [V] = pt->axes[PIVOT_AXIS_COLUMN].label_depth,
755 int n_title = pt->title && pt->show_title;
756 int n_layers = pivot_count_layers (pt);
757 int n_caption = pt->caption && pt->show_caption;
758 int max_footnotes = pt->n_footnotes;
760 int min_body_width = n_title || n_layers || n_caption;
761 int n[TABLE_N_AXES] = {
762 [H] = MAX (min_body_width, stub[H] + data[H]),
763 [V] = n_title + n_layers + stub[V] + data[V] + n_caption + max_footnotes,
766 struct table *t = table_create (n[H], n[V],
767 stub[H], n_title + n_layers + stub[V]);
768 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
769 t->styles[i] = table_area_style_override (
770 t->container, &pt->look->areas[i], NULL, NULL, false);
772 t->n_borders = PIVOT_N_BORDERS;
773 t->borders = pool_nmalloc (t->container, PIVOT_N_BORDERS, sizeof *t->borders);
774 for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
776 const struct table_border_style *src = &pt->look->borders[i];
777 struct table_border_style *dst = &t->borders[i];
778 *dst = (!printing && pt->show_grid_lines && src->stroke == TABLE_STROKE_NONE
779 ? (struct table_border_style) { .stroke = TABLE_STROKE_DASHED,
780 .color = CELL_COLOR_BLACK }
784 int body[TABLE_N_AXES][2] = {
785 [H] = { 0, n[H] - 1 },
788 n_title + n_layers + stub[V] + data[V] - 1
793 put_layers (t, pt, layer_indexes,
795 body[H][1], n_title + n_layers - 1);
798 &pt->axes[PIVOT_AXIS_COLUMN], H, &pt->axes[PIVOT_AXIS_ROW],
799 PIVOT_BORDER_DIM_COL_HORZ,
800 PIVOT_BORDER_DIM_COL_VERT,
801 PIVOT_BORDER_CAT_COL_HORZ,
802 PIVOT_BORDER_CAT_COL_VERT,
803 column_enumeration, data[H],
804 PIVOT_AREA_COLUMN_LABELS,
805 pt->rotate_outer_row_labels, false, body);
808 &pt->axes[PIVOT_AXIS_ROW], V, &pt->axes[PIVOT_AXIS_COLUMN],
809 PIVOT_BORDER_DIM_ROW_VERT,
810 PIVOT_BORDER_DIM_ROW_HORZ,
811 PIVOT_BORDER_CAT_ROW_VERT,
812 PIVOT_BORDER_CAT_ROW_HORZ,
813 row_enumeration, data[V],
814 PIVOT_AREA_ROW_LABELS,
815 false, pt->rotate_inner_column_labels, body);
817 int data_ofs[TABLE_N_AXES] = {
818 [H] = body[H][0] + stub[H],
819 [V] = body[V][0] + stub[V],
822 const size_t *pindexes[PIVOT_N_AXES] = { [PIVOT_AXIS_LAYER] = layer_indexes };
823 size_t *dindexes = XCALLOC (pt->n_dimensions, size_t);
825 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
826 &pt->axes[PIVOT_AXIS_ROW])
829 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
831 &pt->axes[PIVOT_AXIS_COLUMN])
833 pivot_table_convert_indexes_ptod (pt, pindexes, dindexes);
834 const struct pivot_value *value = pivot_table_get (pt, dindexes);
835 fill_cell (t, x, y, PIVOT_AREA_DATA, value, false);
843 if ((pt->corner_text || !pt->look->row_labels_in_corner)
844 && stub[H] && stub[V])
845 fill_cell_spanned (t, body[H][0], body[V][0],
846 body[H][0] + stub[H] - 1, body[V][0] + stub[V] - 1,
847 PIVOT_AREA_CORNER, pt->corner_text, false, 0);
849 if (body[H][1] >= body[H][0] && body[V][1] >= body[V][0])
851 table_hline (t, PIVOT_BORDER_INNER_TOP,
852 body[H][0], body[H][1], body[V][0]);
853 table_hline (t, PIVOT_BORDER_INNER_BOTTOM,
854 body[H][0], body[H][1], body[V][1] + 1);
855 table_vline (t, PIVOT_BORDER_INNER_LEFT,
856 body[H][0], body[V][0], body[V][1]);
857 table_vline (t, PIVOT_BORDER_INNER_RIGHT,
858 body[H][1] + 1, body[V][0], body[V][1]);
861 table_hline (t, PIVOT_BORDER_DATA_TOP,
862 body[H][0], body[H][1], data_ofs[V]);
864 table_vline (t, PIVOT_BORDER_DATA_LEFT,
865 data_ofs[H], body[V][0], body[V][1]);
869 fill_cell_spanned (t,
870 body[H][0], body[V][1] + 1,
871 body[H][1], body[V][1] + 1,
872 PIVOT_AREA_CAPTION, pt->caption, false,
873 TABLE_CELL_FULL_WIDTH);
876 struct pivot_footnote **f = collect_footnotes (pt, t, NULL, NULL, NULL, &nf);
877 assert (nf <= max_footnotes);
878 put_footnotes (t, pt, f, nf, body[H][0], body[H][1],
879 body[V][1] + 1 + n_caption);
880 t->n[V] = body[V][1] + 1 + n_caption + nf;
886 printf ("%d,%d - %d,%d\n",
890 fill_cell_spanned (t,
893 PIVOT_AREA_TITLE, pt->title, false,
894 TABLE_CELL_FULL_WIDTH);
897 free (column_enumeration);
898 free (row_enumeration);
901 printf ("output page:\n");
902 for (int y = 0; y < t->n[V]; y++)
903 for (int x = 0; x < t->n[H]; x++)
905 struct table_cell cell;
907 table_get_cell (t, x, y, &cell);
908 char *s = pivot_value_to_string (cell.value, NULL);
909 printf ("%d,%d - %d,%d: %s\n",
910 cell.d[H][0], cell.d[V][0],
911 cell.d[H][1], cell.d[V][1],
921 pivot_table_submit (struct pivot_table *pt)
923 output_item_submit (table_item_create (pt));