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