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