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