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