bea33985ebf1d7f0d0c22e94b7b7f243ef77927e
[pspp] / src / output / pivot-output.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2018 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include <stdlib.h>
20
21 #include "output/pivot-output.h"
22
23 #include "data/settings.h"
24 #include "libpspp/assertion.h"
25 #include "libpspp/pool.h"
26 #include "output/pivot-table.h"
27 #include "output/table-item.h"
28 #include "output/table-provider.h"
29 #include "output/table.h"
30 #include "output/text-item.h"
31
32 #include "gl/minmax.h"
33 #include "gl/xalloc.h"
34
35 #define H TABLE_HORZ
36 #define V TABLE_VERT
37
38 size_t *
39 pivot_output_next_layer (const struct pivot_table *pt, size_t *indexes,
40                          bool print)
41 {
42   const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
43   if (print && pt->look->print_all_layers)
44     return pivot_axis_iterator_next (indexes, layer_axis);
45   else if (!indexes)
46     {
47       size_t size = layer_axis->n_dimensions * sizeof *pt->current_layer;
48       return size ? xmemdup (pt->current_layer, size) : xmalloc (1);
49     }
50   else
51     {
52       free (indexes);
53       return NULL;
54     }
55 }
56
57 static const struct pivot_category *
58 find_category (const struct pivot_dimension *d, int dim_index,
59                const size_t *indexes, int row_ofs)
60 {
61   size_t index = indexes[dim_index];
62   assert (index < d->n_leaves);
63   for (const struct pivot_category *c = d->presentation_leaves[index];
64        c; c = c->parent)
65     {
66       /* A category can cover multiple rows.  Only return the category for its
67          top row. */
68       if (row_ofs == c->extra_depth)
69         return c;
70
71       row_ofs -= 1 + c->extra_depth;
72       if (row_ofs < 0)
73         return NULL;
74     }
75   return NULL;
76 }
77
78 static struct table_area_style *
79 table_area_style_override (struct pool *pool,
80                            const struct table_area_style *in,
81                            const struct cell_style *cell_,
82                            const struct font_style *font_,
83                            bool rotate_label)
84 {
85   const struct cell_style *cell = cell_ ? cell_ : &in->cell_style;
86   const struct font_style *font = font_ ? font_ : &in->font_style;
87
88   struct table_area_style *out = (pool
89                             ? pool_alloc (pool, sizeof *out)
90                             : xmalloc (sizeof *out));
91   *out = (struct table_area_style) {
92     .cell_style.halign = rotate_label ? TABLE_HALIGN_CENTER : cell->halign,
93     .cell_style.valign = rotate_label ? TABLE_VALIGN_CENTER : cell->valign,
94     .cell_style.decimal_offset = cell->decimal_offset,
95     .cell_style.margin[H][0] = cell->margin[H][0],
96     .cell_style.margin[H][1] = cell->margin[H][1],
97     .cell_style.margin[V][0] = cell->margin[V][0],
98     .cell_style.margin[V][1] = cell->margin[V][1],
99     .font_style.fg[0] = font->fg[0],
100     .font_style.fg[1] = font->fg[1],
101     .font_style.bg[0] = font->bg[0],
102     .font_style.bg[1] = font->bg[1],
103     .font_style.typeface = (font->typeface
104                             ? pool_strdup (pool, font->typeface)
105                             : NULL),
106     .font_style.size = font->size,
107     .font_style.bold = font->bold,
108     .font_style.italic = font->italic,
109     .font_style.underline = font->underline,
110     .font_style.markup = font->markup,
111   };
112   return out;
113 }
114
115 static void
116 fill_cell (struct table *t, int x1, int y1, int x2, int y2,
117            int style_idx, const struct pivot_value *value,
118            bool rotate_label)
119 {
120   int options = style_idx << TAB_STYLE_SHIFT;
121   if (rotate_label)
122     options |= TAB_ROTATE;
123
124   table_put (t, x1, y1, x2, y2, options, value);
125 }
126
127 static void
128 fill_cell_owned (struct table *t, int x1, int y1, int x2, int y2,
129                  int style_idx, struct string *s, bool rotate_label)
130 {
131   int options = style_idx << TAB_STYLE_SHIFT;
132   if (rotate_label)
133     options |= TAB_ROTATE;
134
135   table_put_owned (t, x1, y1, x2, y2, options,
136                    pivot_value_new_user_text_nocopy (ds_steal_cstr (s)));
137 }
138
139 static int
140 get_table_rule (const struct table_border_style *styles,
141                 enum pivot_border style_idx)
142 {
143   return styles[style_idx].stroke | (style_idx << TAB_RULE_STYLE_SHIFT);
144 }
145
146 static void
147 draw_line (struct table *t, const struct table_border_style *styles,
148            enum pivot_border style_idx,
149            enum table_axis axis, int a, int b0, int b1)
150 {
151   int rule = get_table_rule (styles, style_idx);
152   if (axis == H)
153     table_hline (t, rule, b0, b1, a);
154   else
155     table_vline (t, rule, a, b0, b1);
156 }
157
158 /* Fills row or column headings into T.
159
160    This function uses terminology and variable names for column headings, but
161    it also applies to row headings because it uses variables for the
162    differences, e.g. when for column headings it would use the H axis, it
163    instead uses 'h', which is set to H for column headings and V for row
164    headings.  */
165 static void
166 compose_headings (struct table *t,
167                   const struct pivot_axis *h_axis, enum table_axis h,
168                   const struct pivot_axis *v_axis,
169                   const struct table_border_style *borders,
170                   enum pivot_border dim_col_horz,
171                   enum pivot_border dim_col_vert,
172                   enum pivot_border cat_col_horz,
173                   enum pivot_border cat_col_vert,
174                   const size_t *column_enumeration, size_t n_columns,
175                   int label_style_idx,
176                   bool rotate_inner_labels, bool rotate_outer_labels)
177 {
178   const enum table_axis v = !h;
179   const int v_size = h_axis->label_depth;
180   const int h_ofs = v_axis->label_depth;
181
182   if (!h_axis->n_dimensions || !n_columns || !v_size)
183     return;
184
185   const int stride = MAX (1, h_axis->n_dimensions);
186
187   /* Below, we're going to iterate through the dimensions.  Each dimension
188      occupies one or more rows in the heading.  'top_row' is the top row of
189      these (and 'top_row + d->label_depth - 1' is the bottom row). */
190   int top_row = 0;
191
192   /* We're going to iterate through dimensions and the rows that label them
193      from top to bottom (from outer to inner dimensions).  As we move downward,
194      we start drawing vertical rules to separate categories and groups.  After
195      we start drawing a vertical rule in a particular horizontal position, it
196      continues until the bottom of the heading.  vrules[pos] indicates whether,
197      in our current row, we have already started drawing a vertical rule in
198      horizontal position 'pos'.  (There are n_columns + 1 horizontal positions.
199      We allocate all of them for convenience below but only the inner n_columns
200      - 1 of them really matter.)
201
202      Here's an example that shows how vertical rules continue all the way
203      downward:
204
205      +-----------------------------------------------------+ __
206      |                         bbbb                        |  |
207      +-----------------+-----------------+-----------------+  |dimension "bbbb"
208      |      bbbb1      |      bbbb2      |      bbbb3      | _|
209      +-----------------+-----------------+-----------------+ __
210      |       aaaa      |       aaaa      |       aaaa      |  |
211      +-----+-----+-----+-----+-----+-----+-----+-----+-----+  |dimension "aaaa"
212      |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3| _|
213      +-----+-----+-----+-----+-----+-----+-----+-----+-----+
214
215      ^     ^     ^     ^     ^     ^     ^     ^     ^     ^
216      |     |     |     |     |     |     |     |     |     |
217      0     1     2     3     4     5     6     7     8     9
218      |___________________vrules[] indexes__________________|
219
220      Our data structures are more naturally iterated from bottom to top (inner
221      to outer dimensions).  A previous version of this code actually worked
222      like that, but it didn't draw all of the vertical lines correctly as shown
223      above.  It ended up rendering the above heading much like shown below,
224      which isn't what users expect.  The "aaaa" label really needs to be shown
225      three times for clarity:
226
227      +-----------------------------------------------------+
228      |                         bbbb                        |
229      +-----------------+-----------------+-----------------+
230      |      bbbb1      |      bbbb2      |      bbbb3      |
231      +-----------------+-----------------+-----------------+
232      |                 |       aaaa      |                 |
233      +-----+-----+-----+-----+-----+-----+-----+-----+-----+
234      |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|
235      +-----+-----+-----+-----+-----+-----+-----+-----+-----+
236   */
237   bool *vrules = xzalloc (n_columns + 1);
238   vrules[0] = vrules[n_columns] = true;
239   for (int dim_index = h_axis->n_dimensions; --dim_index >= 0; )
240     {
241       const struct pivot_dimension *d = h_axis->dimensions[dim_index];
242       if (d->hide_all_labels)
243         continue;
244
245       for (int row_ofs = 0; row_ofs < d->label_depth; row_ofs++)
246         {
247           for (size_t x1 = 0; x1 < n_columns;)
248             {
249               const struct pivot_category *c = find_category (
250                 d, dim_index, column_enumeration + x1 * stride,
251                 d->label_depth - row_ofs - 1);
252               if (!c)
253                 {
254                   x1++;
255                   continue;
256                 }
257
258               size_t x2;
259               for (x2 = x1 + 1; x2 < n_columns; x2++)
260                 {
261                   if (vrules[x2])
262                     break;
263                   const struct pivot_category *c2 = find_category (
264                     d, dim_index, column_enumeration + x2 * stride,
265                     d->label_depth - row_ofs - 1);
266                   if (c != c2)
267                     break;
268                 }
269
270               int y1 = top_row + row_ofs;
271               int y2 = top_row + row_ofs + c->extra_depth + 1;
272               bool is_outer_row = y1 == 0;
273               bool is_inner_row = y2 == v_size;
274               if (pivot_category_is_leaf (c) || c->show_label)
275                 {
276                   int bb[TABLE_N_AXES][2];
277                   bb[h][0] = x1 + h_ofs;
278                   bb[h][1] = x2 + h_ofs - 1;
279                   bb[v][0] = y1;
280                   bb[v][1] = y2 - 1;
281                   bool rotate = ((rotate_inner_labels && is_inner_row)
282                                  || (rotate_outer_labels && is_outer_row));
283                   fill_cell (t, bb[H][0], bb[V][0], bb[H][1], bb[V][1],
284                              label_style_idx, c->name, rotate);
285
286                   /* Draw all the vertical lines in our running example, other
287                      than the far left and far right ones.  Only the ones that
288                      start in the last row of the heading are drawn with the
289                      "category" style, the rest with the "dimension" style,
290                      e.g. only the # below are category style:
291
292                      +-----------------------------------------------------+
293                      |                         bbbb                        |
294                      +-----------------+-----------------+-----------------+
295                      |      bbbb1      |      bbbb2      |      bbbb3      |
296                      +-----------------+-----------------+-----------------+
297                      |       aaaa      |       aaaa      |       aaaa      |
298                      +-----+-----+-----+-----+-----+-----+-----+-----+-----+
299                      |aaaa1#aaaa2#aaaa3|aaaa1#aaaa2#aaaa3|aaaa1#aaaa2#aaaa3|
300                      +-----+-----+-----+-----+-----+-----+-----+-----+-----+
301                   */
302                   enum pivot_border style
303                     = (y1 == v_size - 1 ? cat_col_vert : dim_col_vert);
304                   if (!vrules[x2])
305                     {
306                       draw_line (t, borders, style, v, x2 + h_ofs, y1,
307                                  t->n[v] - 1);
308                       vrules[x2] = true;
309                     }
310                   if (!vrules[x1])
311                     {
312                       draw_line (t, borders, style, v, x1 + h_ofs, y1,
313                                  t->n[v] - 1);
314                       vrules[x1] = true;
315                     }
316                 }
317
318               /* Draws the horizontal lines within a dimension, that is, those
319                  that separate a separating a category (or group) from its
320                  parent group or dimension's label.  Our running example
321                  doesn't have groups but the ==== lines below show the
322                  separators between categories and their dimension label:
323
324                  +-----------------------------------------------------+
325                  |                         bbbb                        |
326                  +=================+=================+=================+
327                  |      bbbb1      |      bbbb2      |      bbbb3      |
328                  +-----------------+-----------------+-----------------+
329                  |       aaaa      |       aaaa      |       aaaa      |
330                  +=====+=====+=====+=====+=====+=====+=====+=====+=====+
331                  |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|
332                  +-----+-----+-----+-----+-----+-----+-----+-----+-----+
333               */
334               if (c->parent && c->parent->show_label)
335                 draw_line (t, borders, cat_col_horz, h, y1,
336                            x1 + h_ofs, x2 + h_ofs - 1);
337               x1 = x2;
338             }
339         }
340
341       if (d->root->show_label_in_corner && h_ofs > 0)
342         {
343           int bb[TABLE_N_AXES][2];
344           bb[h][0] = 0;
345           bb[h][1] = h_ofs - 1;
346           bb[v][0] = top_row;
347           bb[v][1] = top_row + d->label_depth - 1;
348           fill_cell (t, bb[H][0], bb[V][0], bb[H][1], bb[V][1],
349                      PIVOT_AREA_CORNER, d->root->name, false);
350         }
351
352       /* Draw the horizontal line between dimensions, e.g. the ===== line here:
353
354          +-----------------------------------------------------+ __
355          |                         bbbb                        |  |
356          +-----------------+-----------------+-----------------+  |dim "bbbb"
357          |      bbbb1      |      bbbb2      |      bbbb3      | _|
358          +=================+=================+=================+ __
359          |       aaaa      |       aaaa      |       aaaa      |  |
360          +-----+-----+-----+-----+-----+-----+-----+-----+-----+  |dim "aaaa"
361          |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3| _|
362          +-----+-----+-----+-----+-----+-----+-----+-----+-----+
363       */
364       if (dim_index != h_axis->n_dimensions - 1)
365         draw_line (t, borders, dim_col_horz, h, top_row, h_ofs,
366                    t->n[h] - 1);
367       top_row += d->label_depth;
368     }
369   free (vrules);
370 }
371
372 static struct table *
373 create_aux_table (const struct pivot_table *pt, int nc, int nr,
374                   int style_idx)
375 {
376   struct table *table = table_create (nc, nr, 0, 0, 0, 0);
377   table->styles[style_idx] = table_area_style_override (
378       table->container, &pt->look->areas[style_idx], NULL, NULL, false);
379   return table;
380 }
381
382
383 static void
384 add_references (const struct pivot_table *pt, const struct table *table,
385                 bool *refs, size_t *n_refs)
386 {
387   if (!table)
388     return;
389
390   for (int y = 0; y < table->n[V]; y++)
391     for (int x = 0; x < table->n[H]; )
392       {
393         struct table_cell cell;
394         table_get_cell (table, x, y, &cell);
395
396         if (x == cell.d[H][0] && y == cell.d[V][0])
397           {
398             for (size_t i = 0; i < cell.value->n_footnotes; i++)
399               {
400                 size_t idx = cell.value->footnote_indexes[i];
401                 assert (idx < pt->n_footnotes);
402
403                 if (!refs[idx] && pt->footnotes[idx]->show)
404                   {
405                     refs[idx] = true;
406                     (*n_refs)++;
407                   }
408               }
409           }
410
411         x = cell.d[TABLE_HORZ][1];
412       }
413 }
414
415 static struct pivot_footnote **
416 collect_footnotes (const struct pivot_table *pt,
417                    const struct table *title,
418                    const struct table *layers,
419                    const struct table *body,
420                    const struct table *caption,
421                    size_t *n_footnotesp)
422 {
423   if (!pt->n_footnotes)
424     {
425       *n_footnotesp = 0;
426       return NULL;
427     }
428
429   bool *refs = xzalloc (pt->n_footnotes);
430   size_t n_refs = 0;
431   add_references (pt, title, refs, &n_refs);
432   add_references (pt, layers, refs, &n_refs);
433   add_references (pt, body, refs, &n_refs);
434   add_references (pt, caption, refs, &n_refs);
435
436   struct pivot_footnote **footnotes = xnmalloc (n_refs, sizeof *footnotes);
437   size_t n_footnotes = 0;
438   for (size_t i = 0; i < pt->n_footnotes; i++)
439     if (refs[i])
440       footnotes[n_footnotes++] = pt->footnotes[i];
441   assert (n_footnotes == n_refs);
442
443   free (refs);
444
445   *n_footnotesp = n_footnotes;
446   return footnotes;
447 }
448
449 void
450 pivot_output (const struct pivot_table *pt,
451               const size_t *layer_indexes,
452               bool printing UNUSED,
453               struct table **titlep,
454               struct table **layersp,
455               struct table **bodyp,
456               struct table **captionp,
457               struct table **footnotesp,
458               struct pivot_footnote ***fp, size_t *nfp)
459 {
460   const size_t *pindexes[PIVOT_N_AXES]
461     = { [PIVOT_AXIS_LAYER] = layer_indexes };
462
463   size_t data[TABLE_N_AXES];
464   size_t *column_enumeration = pivot_table_enumerate_axis (
465     pt, PIVOT_AXIS_COLUMN, layer_indexes, pt->look->omit_empty, &data[H]);
466   size_t *row_enumeration = pivot_table_enumerate_axis (
467     pt, PIVOT_AXIS_ROW, layer_indexes, pt->look->omit_empty, &data[V]);
468
469   int stub[TABLE_N_AXES] = {
470     [H] = pt->axes[PIVOT_AXIS_ROW].label_depth,
471     [V] = pt->axes[PIVOT_AXIS_COLUMN].label_depth,
472   };
473   struct table *body = table_create (data[H] + stub[H],
474                                      data[V] + stub[V],
475                                      stub[H], 0, stub[V], 0);
476   for (size_t i = 0; i < PIVOT_N_AREAS; i++)
477     body->styles[i] = table_area_style_override (
478       body->container, &pt->look->areas[i], NULL, NULL, false);
479
480   struct table_border_style borders[PIVOT_N_BORDERS];
481   memcpy (borders, pt->look->borders, sizeof borders);
482   if (!printing && pt->show_grid_lines)
483     for (int b = 0; b < PIVOT_N_BORDERS; b++)
484       if (borders[b].stroke == TABLE_STROKE_NONE)
485         borders[b].stroke = TABLE_STROKE_DASHED;
486
487   for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
488     {
489       const struct table_border_style *in = &pt->look->borders[i];
490       body->rule_colors[i] = pool_alloc (body->container,
491                                          sizeof *body->rule_colors[i]);
492       *body->rule_colors[i] = in->color;
493     }
494
495   compose_headings (body,
496                     &pt->axes[PIVOT_AXIS_COLUMN], H, &pt->axes[PIVOT_AXIS_ROW],
497                     borders,
498                     PIVOT_BORDER_DIM_COL_HORZ,
499                     PIVOT_BORDER_DIM_COL_VERT,
500                     PIVOT_BORDER_CAT_COL_HORZ,
501                     PIVOT_BORDER_CAT_COL_VERT,
502                     column_enumeration, data[H],
503                     PIVOT_AREA_COLUMN_LABELS,
504                     pt->rotate_outer_row_labels, false);
505
506   compose_headings (body,
507                     &pt->axes[PIVOT_AXIS_ROW], V, &pt->axes[PIVOT_AXIS_COLUMN],
508                     borders,
509                     PIVOT_BORDER_DIM_ROW_VERT,
510                     PIVOT_BORDER_DIM_ROW_HORZ,
511                     PIVOT_BORDER_CAT_ROW_VERT,
512                     PIVOT_BORDER_CAT_ROW_HORZ,
513                     row_enumeration, data[V],
514                     PIVOT_AREA_ROW_LABELS,
515                     false, pt->rotate_inner_column_labels);
516
517   size_t *dindexes = XCALLOC (pt->n_dimensions, size_t);
518   size_t y = 0;
519   PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
520                               &pt->axes[PIVOT_AXIS_ROW])
521     {
522       size_t x = 0;
523       PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
524                                   column_enumeration,
525                                   &pt->axes[PIVOT_AXIS_COLUMN])
526         {
527           pivot_table_convert_indexes_ptod (pt, pindexes, dindexes);
528           const struct pivot_value *value = pivot_table_get (pt, dindexes);
529           fill_cell (body, x + stub[H], y + stub[V], x + stub[H], y + stub[V],
530                      PIVOT_AREA_DATA, value, false);
531
532           x++;
533         }
534
535       y++;
536     }
537   free (dindexes);
538
539   if ((pt->corner_text || !pt->look->row_labels_in_corner)
540       && stub[H] && stub[V])
541     fill_cell (body, 0, 0, stub[H] - 1, stub[V] - 1,
542                PIVOT_AREA_CORNER, pt->corner_text, false);
543
544   if (body->n[H] && body->n[V])
545     {
546       table_hline (
547         body, get_table_rule (borders, PIVOT_BORDER_INNER_TOP),
548         0, body->n[H] - 1, 0);
549       table_hline (
550         body, get_table_rule (borders, PIVOT_BORDER_INNER_BOTTOM),
551         0, body->n[H] - 1, body->n[V]);
552       table_vline (
553         body, get_table_rule (borders, PIVOT_BORDER_INNER_LEFT),
554         0, 0, body->n[V] - 1);
555       table_vline (
556         body, get_table_rule (borders, PIVOT_BORDER_INNER_RIGHT),
557         body->n[H], 0, body->n[V] - 1);
558
559       if (stub[V])
560         table_hline (
561           body, get_table_rule (borders, PIVOT_BORDER_DATA_TOP),
562           0, body->n[H] - 1, stub[V]);
563       if (stub[H])
564         table_vline (
565           body, get_table_rule (borders, PIVOT_BORDER_DATA_LEFT),
566           stub[H], 0, body->n[V] - 1);
567
568     }
569   free (column_enumeration);
570   free (row_enumeration);
571
572   /* Title. */
573   struct table *title;
574   if (pt->title && pt->show_title && titlep)
575     {
576       title = create_aux_table (pt, 1, 1, PIVOT_AREA_TITLE);
577       fill_cell (title, 0, 0, 0, 0, PIVOT_AREA_TITLE, pt->title, false);
578     }
579   else
580     title = NULL;
581
582   /* Layers. */
583   const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
584   int n_layers = 0;
585   if (layersp)
586     for (size_t i = 0; i < layer_axis->n_dimensions; i++)
587       {
588         const struct pivot_dimension *d = layer_axis->dimensions[i];
589         if (d->n_leaves)
590           n_layers++;
591       }
592
593   struct table *layers;
594   if (n_layers > 0)
595     {
596       layers = create_aux_table (pt, 1, n_layers, PIVOT_AREA_LAYERS);
597       size_t y = n_layers - 1;
598       for (size_t i = 0; i < layer_axis->n_dimensions; i++)
599         {
600           const struct pivot_dimension *d = layer_axis->dimensions[i];
601           if (!d->n_leaves)
602             continue;
603
604           struct string s = DS_EMPTY_INITIALIZER;
605           pivot_value_format (d->data_leaves[layer_indexes[i]]->name, pt, &s);
606           fill_cell_owned (layers, 0, y, 0, y, PIVOT_AREA_LAYERS, &s, false);
607           y--;
608         }
609     }
610   else
611     layers = NULL;
612
613   /* Caption. */
614   struct table *caption;
615   if (pt->caption && pt->show_caption && captionp)
616     {
617       caption = create_aux_table (pt, 1, 1, PIVOT_AREA_CAPTION);
618       fill_cell (caption, 0, 0, 0, 0, PIVOT_AREA_CAPTION, pt->caption, false);
619     }
620   else
621     caption = NULL;
622
623   /* Footnotes. */
624   size_t nf;
625   struct pivot_footnote **f = collect_footnotes (pt, title, layers, body,
626                                                  caption, &nf);
627   struct table *footnotes;
628   if (nf && footnotesp)
629     {
630       footnotes = create_aux_table (pt, 1, nf, PIVOT_AREA_FOOTER);
631
632       for (size_t i = 0; i < nf; i++)
633         {
634           struct string s = DS_EMPTY_INITIALIZER;
635           pivot_footnote_format_marker (f[i], pt, &s);
636           ds_put_cstr (&s, ". ");
637           pivot_value_format (f[i]->content, pt, &s);
638           fill_cell_owned (footnotes, 0, i, 0, i, PIVOT_AREA_FOOTER, &s,
639                            false);
640         }
641     }
642   else
643     footnotes = NULL;
644
645   *titlep = title;
646   if (layersp)
647     *layersp = layers;
648   *bodyp = body;
649   if (captionp)
650     *captionp = caption;
651   if (footnotesp)
652     *footnotesp = footnotes;
653   if (fp)
654     {
655       *fp = f;
656       *nfp = nf;
657     }
658   else
659     free (f);
660 }
661
662 void
663 pivot_table_submit (struct pivot_table *pt)
664 {
665   table_item_submit (table_item_create (pt));
666 }