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