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, 0, 0);
378 table->styles[style_idx] = table_area_style_override (
379 table->container, &pt->look->areas[style_idx], NULL, NULL, false);
384 add_references (const struct pivot_table *pt, const struct table *table,
385 bool *refs, size_t *n_refs)
390 for (int y = 0; y < table->n[V]; y++)
391 for (int x = 0; x < table->n[H]; )
393 struct table_cell cell;
394 table_get_cell (table, x, y, &cell);
396 if (x == cell.d[H][0] && y == cell.d[V][0])
398 const struct pivot_value_ex *ex = pivot_value_ex (cell.value);
399 for (size_t i = 0; i < ex->n_footnotes; i++)
401 size_t idx = ex->footnote_indexes[i];
402 assert (idx < pt->n_footnotes);
404 if (!refs[idx] && pt->footnotes[idx]->show)
412 x = cell.d[TABLE_HORZ][1];
416 static struct pivot_footnote **
417 collect_footnotes (const struct pivot_table *pt,
418 const struct table *title,
419 const struct table *layers,
420 const struct table *body,
421 const struct table *caption,
422 size_t *n_footnotesp)
424 if (!pt->n_footnotes)
430 bool *refs = XCALLOC (pt->n_footnotes, bool);
432 add_references (pt, title, refs, &n_refs);
433 add_references (pt, layers, refs, &n_refs);
434 add_references (pt, body, refs, &n_refs);
435 add_references (pt, caption, refs, &n_refs);
437 struct pivot_footnote **footnotes = xnmalloc (n_refs, sizeof *footnotes);
438 size_t n_footnotes = 0;
439 for (size_t i = 0; i < pt->n_footnotes; i++)
441 footnotes[n_footnotes++] = pt->footnotes[i];
442 assert (n_footnotes == n_refs);
446 *n_footnotesp = n_footnotes;
450 static struct table *
451 pivot_output_title (const struct pivot_table *pt)
453 if (!pt->title || !pt->show_title)
455 struct table *title = create_aux_table (pt, 1, 1, PIVOT_AREA_TITLE);
456 fill_cell (title, 0, 0, PIVOT_AREA_TITLE, pt->title, false);
461 pivot_count_layers (const struct pivot_table *pt)
463 const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
465 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
467 const struct pivot_dimension *d = layer_axis->dimensions[i];
475 put_layers (struct table *t, const struct pivot_table *pt,
476 const size_t *layer_indexes, int x1, int y1 UNUSED, int x2, int y2)
478 assert (y1 + pivot_count_layers (pt) - 1 == y2);
479 const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
480 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
482 const struct pivot_dimension *d = layer_axis->dimensions[i];
486 struct string s = DS_EMPTY_INITIALIZER;
487 pivot_value_format (d->data_leaves[layer_indexes[i]]->name, pt, &s);
489 fill_cell_owned (t, x1, y, x2, y, PIVOT_AREA_LAYERS, &s, false);
493 static struct table *
494 pivot_output_layers (const struct pivot_table *pt, const size_t *layer_indexes)
496 int n_layers = pivot_count_layers (pt);
500 struct table *layers = create_aux_table (pt, 1, n_layers, PIVOT_AREA_LAYERS);
501 put_layers (layers, pt, layer_indexes, 0, 0, 0, n_layers - 1);
505 static struct table *
506 pivot_output_caption (const struct pivot_table *pt)
508 if (!pt->caption || !pt->show_caption)
511 struct table *caption = create_aux_table (pt, 1, 1, PIVOT_AREA_CAPTION);
512 fill_cell (caption, 0, 0, PIVOT_AREA_CAPTION, pt->caption, false);
517 put_footnotes (struct table *t, const struct pivot_table *pt,
518 struct pivot_footnote **f, size_t nf,
519 int x1, int x2, int y1)
521 for (size_t i = 0; i < nf; i++)
523 struct string s = DS_EMPTY_INITIALIZER;
524 pivot_footnote_format_marker (f[i], pt, &s);
525 ds_put_cstr (&s, ". ");
526 pivot_value_format (f[i]->content, pt, &s);
528 fill_cell_owned (t, x1, y, x2, y, PIVOT_AREA_FOOTER, &s, false);
532 static struct table *
533 pivot_output_footnotes (const struct pivot_table *pt,
534 struct pivot_footnote **f, size_t nf)
539 struct table *footnotes = create_aux_table (pt, 1, nf, PIVOT_AREA_FOOTER);
540 put_footnotes (footnotes, pt, f, nf, 0, 0, 0);
545 pivot_output (const struct pivot_table *pt,
546 const size_t *layer_indexes,
548 struct table **titlep,
549 struct table **layersp,
550 struct table **bodyp,
551 struct table **captionp,
552 struct table **footnotesp,
553 struct pivot_footnote ***fp, size_t *nfp)
555 const size_t *pindexes[PIVOT_N_AXES]
556 = { [PIVOT_AXIS_LAYER] = layer_indexes };
558 size_t data[TABLE_N_AXES];
559 size_t *column_enumeration = pivot_table_enumerate_axis (
560 pt, PIVOT_AXIS_COLUMN, layer_indexes, pt->look->omit_empty, &data[H]);
561 size_t *row_enumeration = pivot_table_enumerate_axis (
562 pt, PIVOT_AXIS_ROW, layer_indexes, pt->look->omit_empty, &data[V]);
564 int stub[TABLE_N_AXES] = {
565 [H] = pt->axes[PIVOT_AXIS_ROW].label_depth,
566 [V] = pt->axes[PIVOT_AXIS_COLUMN].label_depth,
568 int frame[TABLE_N_AXES][2] = {
570 pt->look->borders[PIVOT_BORDER_OUTER_LEFT].stroke != TABLE_STROKE_NONE,
571 pt->look->borders[PIVOT_BORDER_OUTER_RIGHT].stroke != TABLE_STROKE_NONE,
574 pt->look->borders[PIVOT_BORDER_OUTER_TOP].stroke != TABLE_STROKE_NONE,
575 pt->look->borders[PIVOT_BORDER_OUTER_BOTTOM].stroke != TABLE_STROKE_NONE,
578 int fs[TABLE_N_AXES] = {
579 [H] = stub[H] + frame[H][0],
580 [V] = stub[V] + frame[V][0],
583 struct table *body = table_create (
584 frame[H][0] + stub[H] + data[H] + frame[H][1],
585 frame[V][0] + stub[V] + data[V] + frame[V][1],
586 frame[H][0] + stub[H], frame[H][1],
587 frame[V][0] + stub[V], frame[V][1]);
588 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
589 body->styles[i] = table_area_style_override (
590 body->container, &pt->look->areas[i], NULL, NULL, false);
592 body->n_borders = PIVOT_N_BORDERS;
593 body->borders = pool_nmalloc (body->container, PIVOT_N_BORDERS,
594 sizeof *body->borders);
595 for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
597 const struct table_border_style *src = &pt->look->borders[i];
598 struct table_border_style *dst = &body->borders[i];
599 *dst = (!printing && pt->show_grid_lines && src->stroke == TABLE_STROKE_NONE
600 ? (struct table_border_style) { .stroke = TABLE_STROKE_DASHED,
601 .color = CELL_COLOR_BLACK }
605 int body_area[TABLE_N_AXES][2] = {
606 [H] = { frame[H][0], body->n[H] - frame[H][1] - 1 },
607 [V] = { frame[V][0], body->n[V] - frame[V][1] - 1 },
609 compose_headings (body,
610 &pt->axes[PIVOT_AXIS_COLUMN], H, &pt->axes[PIVOT_AXIS_ROW],
611 PIVOT_BORDER_DIM_COL_HORZ,
612 PIVOT_BORDER_DIM_COL_VERT,
613 PIVOT_BORDER_CAT_COL_HORZ,
614 PIVOT_BORDER_CAT_COL_VERT,
615 column_enumeration, data[H],
616 PIVOT_AREA_COLUMN_LABELS,
617 pt->rotate_outer_row_labels, false, body_area);
619 compose_headings (body,
620 &pt->axes[PIVOT_AXIS_ROW], V, &pt->axes[PIVOT_AXIS_COLUMN],
621 PIVOT_BORDER_DIM_ROW_VERT,
622 PIVOT_BORDER_DIM_ROW_HORZ,
623 PIVOT_BORDER_CAT_ROW_VERT,
624 PIVOT_BORDER_CAT_ROW_HORZ,
625 row_enumeration, data[V],
626 PIVOT_AREA_ROW_LABELS,
627 false, pt->rotate_inner_column_labels, body_area);
629 size_t *dindexes = XCALLOC (pt->n_dimensions, size_t);
631 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
632 &pt->axes[PIVOT_AXIS_ROW])
635 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
637 &pt->axes[PIVOT_AXIS_COLUMN])
639 pivot_table_convert_indexes_ptod (pt, pindexes, dindexes);
640 const struct pivot_value *value = pivot_table_get (pt, dindexes);
641 fill_cell (body, x + fs[H], y + fs[V],
642 PIVOT_AREA_DATA, value, false);
651 if ((pt->corner_text || !pt->look->row_labels_in_corner)
652 && stub[H] && stub[V])
653 fill_cell_spanned (body, 0, 0, fs[H] - 1, fs[V] - 1,
654 PIVOT_AREA_CORNER, pt->corner_text, false,
655 TABLE_CELL_FULL_WIDTH);
657 if (body->n[H] && body->n[V])
659 int inner[TABLE_N_AXES][2] = {
660 [H] = { frame[H][0], body->n[H] - frame[H][1] },
661 [V] = { frame[V][0], body->n[V] - frame[V][1] },
663 table_hline (body, PIVOT_BORDER_INNER_TOP,
664 inner[H][0], inner[H][1] - 1, inner[V][0]);
665 table_hline (body, PIVOT_BORDER_INNER_BOTTOM,
666 inner[H][0], inner[H][1] - 1, inner[V][1]);
667 table_vline (body, PIVOT_BORDER_INNER_LEFT,
668 inner[H][0], inner[V][0], inner[V][1] - 1);
669 table_vline (body, PIVOT_BORDER_INNER_LEFT,
670 inner[H][1], inner[V][0], inner[V][1] - 1);
673 table_hline (body, PIVOT_BORDER_DATA_TOP,
674 inner[H][0], inner[H][1] - 1, fs[V]);
676 table_vline (body, PIVOT_BORDER_DATA_LEFT,
677 fs[H], inner[V][0], inner[V][1] - 1);
682 for (int y = 0; y < body->n[V]; y++)
683 table_put (body, 0, y, 0, y, TABLE_CELL_PADDING, NULL);
684 table_vline (body, PIVOT_BORDER_OUTER_LEFT, 0, 0, body->n[V] - 1);
688 int x = body->n[H] - 1;
689 for (int y = 0; y < body->n[V]; y++)
690 table_put (body, x, y, x, y, TABLE_CELL_PADDING, NULL);
691 table_vline (body, PIVOT_BORDER_OUTER_RIGHT,
692 body->n[H], 0, body->n[V] - 1);
696 for (int x = 0; x < body->n[H]; x++)
697 table_put (body, x, 0, x, 0, TABLE_CELL_PADDING, NULL);
698 table_hline (body, PIVOT_BORDER_OUTER_TOP, 0, body->n[H] - 1, 0);
702 int y = body->n[V] - 1;
703 for (int x = 0; x < body->n[H]; x++)
704 table_put (body, x, y, x, y, TABLE_CELL_PADDING, NULL);
705 table_hline (body, PIVOT_BORDER_OUTER_BOTTOM, 0, body->n[H] - 1, body->n[V]);
708 free (column_enumeration);
709 free (row_enumeration);
713 *titlep = pivot_output_title (pt);
715 *layersp = pivot_output_layers (pt, layer_indexes);
717 *captionp = pivot_output_caption (pt);
719 if (fp || footnotesp)
722 struct pivot_footnote **f = collect_footnotes (
723 pt, titlep ? *titlep : NULL, layersp ? *layersp : NULL, body,
724 captionp ? *captionp : NULL, &nf);
727 *footnotesp = pivot_output_footnotes (pt, f, nf);
740 pivot_output_monolithic (const struct pivot_table *pt,
741 const size_t *layer_indexes,
746 #============ Frame ============#
749 # +--- Body ------------------+ #
751 # | +--------------------+ #
756 # +------+--------------------+ #
759 #===============================#
761 The regions are the following size:
763 - Frame: Either 0 or 1 row or column wide, depending on whether the table
764 style has an outer border on each side. The frame cells are always
765 empty and marked as TABLE_CELL_PADDING.
767 - Title: Either 0 or 1 row high.
769 - Layers: Usually, one row for each layer dimension, but less if a layer
770 dimension is empty (has no value).
772 - Body and data: Variable (and can be empty).
774 - Caption: Either 0 or 1 row high.
776 - Footnotes: From zero rows up to the number of footnotes in PT (only
777 footnotes referenced in the other regions are shown).
779 The title, layers, caption, and footnote Region join all the body's
780 columns into single cells.
783 /* Size of data region. */
784 size_t data[TABLE_N_AXES];
785 size_t *column_enumeration = pivot_table_enumerate_axis (
786 pt, PIVOT_AXIS_COLUMN, layer_indexes, pt->look->omit_empty, &data[H]);
787 size_t *row_enumeration = pivot_table_enumerate_axis (
788 pt, PIVOT_AXIS_ROW, layer_indexes, pt->look->omit_empty, &data[V]);
790 /* Size of stub region. */
791 int stub[TABLE_N_AXES] = {
792 [H] = pt->axes[PIVOT_AXIS_ROW].label_depth,
793 [V] = pt->axes[PIVOT_AXIS_COLUMN].label_depth,
796 /* Width of each side of the frame. */
797 int frame[TABLE_N_AXES][2] = {
799 pt->look->borders[PIVOT_BORDER_OUTER_LEFT].stroke != TABLE_STROKE_NONE,
800 pt->look->borders[PIVOT_BORDER_OUTER_RIGHT].stroke != TABLE_STROKE_NONE,
803 pt->look->borders[PIVOT_BORDER_OUTER_TOP].stroke != TABLE_STROKE_NONE,
804 pt->look->borders[PIVOT_BORDER_OUTER_BOTTOM].stroke != TABLE_STROKE_NONE,
808 int n_title = pt->title && pt->show_title;
809 int n_layers = pivot_count_layers (pt);
810 int n_caption = pt->caption && pt->show_caption;
811 int max_footnotes = pt->n_footnotes;
813 int min_body_width = n_title || n_layers || n_caption;
814 int n[TABLE_N_AXES] = {
815 [H] = frame[H][0] + MAX (min_body_width, stub[H] + data[H]) + frame[H][1],
816 [V] = frame[V][0] + n_title + n_layers + stub[V] + data[V] + n_caption + max_footnotes + frame[V][1],
819 struct table *t = table_create (
823 frame[H][0] + stub[H], frame[H][1],
824 frame[V][0] + n_title + n_layers + stub[V], frame[V][1]
829 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
830 t->styles[i] = table_area_style_override (
831 t->container, &pt->look->areas[i], NULL, NULL, false);
833 t->n_borders = PIVOT_N_BORDERS;
834 t->borders = pool_nmalloc (t->container, PIVOT_N_BORDERS, sizeof *t->borders);
835 for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
837 const struct table_border_style *src = &pt->look->borders[i];
838 struct table_border_style *dst = &t->borders[i];
839 *dst = (!printing && pt->show_grid_lines && src->stroke == TABLE_STROKE_NONE
840 ? (struct table_border_style) { .stroke = TABLE_STROKE_DASHED,
841 .color = CELL_COLOR_BLACK }
845 int body[TABLE_N_AXES][2] = {
846 [H] = { frame[H][0], n[H] - frame[H][1] - 1 },
848 frame[V][0] + n_title + n_layers,
849 frame[V][0] + n_title + n_layers + stub[V] + data[V] - 1
854 put_layers (t, pt, layer_indexes,
855 body[H][0], frame[V][0] + n_title,
856 body[H][1], frame[V][0] + n_title + n_layers - 1);
859 &pt->axes[PIVOT_AXIS_COLUMN], H, &pt->axes[PIVOT_AXIS_ROW],
860 PIVOT_BORDER_DIM_COL_HORZ,
861 PIVOT_BORDER_DIM_COL_VERT,
862 PIVOT_BORDER_CAT_COL_HORZ,
863 PIVOT_BORDER_CAT_COL_VERT,
864 column_enumeration, data[H],
865 PIVOT_AREA_COLUMN_LABELS,
866 pt->rotate_outer_row_labels, false, body);
869 &pt->axes[PIVOT_AXIS_ROW], V, &pt->axes[PIVOT_AXIS_COLUMN],
870 PIVOT_BORDER_DIM_ROW_VERT,
871 PIVOT_BORDER_DIM_ROW_HORZ,
872 PIVOT_BORDER_CAT_ROW_VERT,
873 PIVOT_BORDER_CAT_ROW_HORZ,
874 row_enumeration, data[V],
875 PIVOT_AREA_ROW_LABELS,
876 false, pt->rotate_inner_column_labels, body);
878 int data_ofs[TABLE_N_AXES] = {
879 [H] = body[H][0] + stub[H],
880 [V] = body[V][0] + stub[V],
883 const size_t *pindexes[PIVOT_N_AXES] = { [PIVOT_AXIS_LAYER] = layer_indexes };
884 size_t *dindexes = XCALLOC (pt->n_dimensions, size_t);
886 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
887 &pt->axes[PIVOT_AXIS_ROW])
890 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
892 &pt->axes[PIVOT_AXIS_COLUMN])
894 pivot_table_convert_indexes_ptod (pt, pindexes, dindexes);
895 const struct pivot_value *value = pivot_table_get (pt, dindexes);
896 fill_cell (t, x, y, PIVOT_AREA_DATA, value, false);
904 if ((pt->corner_text || !pt->look->row_labels_in_corner)
905 && stub[H] && stub[V])
906 fill_cell_spanned (t, body[H][0], body[V][0],
907 body[H][0] + stub[H] - 1, body[V][0] + stub[V] - 1,
908 PIVOT_AREA_CORNER, pt->corner_text, false,
909 TABLE_CELL_FULL_WIDTH);
911 if (body[H][1] >= body[H][0] && body[V][1] >= body[V][0])
913 table_hline (t, PIVOT_BORDER_INNER_TOP,
914 body[H][0], body[H][1], body[V][0]);
915 table_hline (t, PIVOT_BORDER_INNER_BOTTOM,
916 body[H][0], body[H][1], body[V][1] + 1);
917 table_vline (t, PIVOT_BORDER_INNER_LEFT,
918 body[H][0], body[V][0], body[V][1]);
919 table_vline (t, PIVOT_BORDER_INNER_RIGHT,
920 body[H][1] + 1, body[V][0], body[V][1]);
923 table_hline (t, PIVOT_BORDER_DATA_TOP,
924 body[H][0], body[H][1], data_ofs[V]);
926 table_vline (t, PIVOT_BORDER_DATA_LEFT,
927 data_ofs[H], body[V][0], body[V][1]);
931 fill_cell_spanned (t,
932 body[H][0], body[V][1] + 1,
933 body[H][1], body[V][1] + 1,
934 PIVOT_AREA_CAPTION, pt->caption, false,
935 TABLE_CELL_FULL_WIDTH);
938 struct pivot_footnote **f = collect_footnotes (pt, t, NULL, NULL, NULL, &nf);
939 assert (nf <= max_footnotes);
940 put_footnotes (t, pt, f, nf, body[H][0], body[H][1],
941 body[V][1] + 1 + n_caption);
942 t->n[V] = body[V][1] + 1 + n_caption + nf + frame[V][1];
947 printf ("%d,%d - %d,%d\n",
948 body[H][0], frame[V][0],
949 body[H][1], frame[V][0]);
950 fill_cell_spanned (t,
951 body[H][0], frame[V][0],
952 body[H][1], frame[V][0],
953 PIVOT_AREA_TITLE, pt->title, false,
954 TABLE_CELL_FULL_WIDTH);
959 for (int y = 0; y < t->n[V]; y++)
960 table_put (t, 0, y, 0, y, TABLE_CELL_PADDING, NULL);
961 table_vline (t, PIVOT_BORDER_OUTER_LEFT, 0, 0, t->n[V] - 1);
966 for (int y = 0; y < t->n[V]; y++)
967 table_put (t, x, y, x, y, TABLE_CELL_PADDING, NULL);
968 table_vline (t, PIVOT_BORDER_OUTER_RIGHT,
969 t->n[H], 0, t->n[V] - 1);
973 for (int x = 0; x < t->n[H]; x++)
974 table_put (t, x, 0, x, 0, TABLE_CELL_PADDING, NULL);
975 table_hline (t, PIVOT_BORDER_OUTER_TOP, 0, t->n[H] - 1, 0);
980 for (int x = 0; x < t->n[H]; x++)
981 table_put (t, x, y, x, y, TABLE_CELL_PADDING, NULL);
982 table_hline (t, PIVOT_BORDER_OUTER_TOP, 0, t->n[H] - 1, t->n[V]);
985 free (column_enumeration);
986 free (row_enumeration);
988 printf ("output page:\n");
989 for (int y = 0; y < t->n[V]; y++)
990 for (int x = 0; x < t->n[H]; x++)
992 struct table_cell cell;
994 table_get_cell (t, x, y, &cell);
995 char *s = pivot_value_to_string (cell.value, NULL);
996 printf ("%d,%d - %d,%d: %s\n",
997 cell.d[H][0], cell.d[V][0],
998 cell.d[H][1], cell.d[V][1],
1007 pivot_table_submit (struct pivot_table *pt)
1009 output_item_submit (table_item_create (pt));