23b47c0d54c5720963b65ff23b268a112da25ded
[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   for (int dim_index = a_axis->n_dimensions; --dim_index >= 0; )
272     {
273       const struct pivot_dimension *d = a_axis->dimensions[dim_index];
274       if (d->hide_all_labels)
275         continue;
276
277       for (int row_ofs = 0; row_ofs < d->label_depth; row_ofs++)
278         {
279           for (size_t x1 = 0; x1 < n_columns;)
280             {
281               const struct pivot_category *c = find_category (
282                 d, dim_index, column_enumeration + x1 * stride,
283                 d->label_depth - row_ofs - 1);
284               if (!c)
285                 {
286                   x1++;
287                   continue;
288                 }
289
290               size_t x2;
291               for (x2 = x1 + 1; x2 < n_columns; x2++)
292                 {
293                   if (vrules[x2])
294                     break;
295                   const struct pivot_category *c2 = find_category (
296                     d, dim_index, column_enumeration + x2 * stride,
297                     d->label_depth - row_ofs - 1);
298                   if (c != c2)
299                     break;
300                 }
301
302               int y1 = top_row + row_ofs;
303               int y2 = top_row + row_ofs + c->extra_depth + 1;
304               bool is_outer_row = y1 == 0;
305               bool is_inner_row = y2 == b_size;
306               if (pivot_category_is_leaf (c) || c->show_label)
307                 {
308                   int bb[TABLE_N_AXES][2];
309                   bb[a][0] = x1 + a_ofs;
310                   bb[a][1] = x2 + a_ofs - 1;
311                   bb[b][0] = y1;
312                   bb[b][1] = y2 - 1;
313                   bool rotate = ((rotate_inner_labels && is_inner_row)
314                                  || (rotate_outer_labels && is_outer_row));
315                   fill_cell (t, bb[H][0], bb[V][0], bb[H][1], bb[V][1],
316                              label_style, label_style_idx, c->name, footnotes,
317                              show_values, show_variables, rotate);
318
319                   if (pivot_category_is_leaf (c) && x2 + 1 <= n_columns)
320                     {
321                       enum pivot_border style
322                         = (y1 == 0 && a_axis->label_depth > d->label_depth
323                            ? dim_col_vert
324                            : cat_col_vert);
325                       draw_line (t, borders, style, b, x2 + a_ofs, y1,
326                                  t->n[b] - 1);
327                       vrules[x2] = true;
328                     }
329                   if (pivot_category_is_leaf (c) && x1 > 0)
330                     {
331                       enum pivot_border style
332                         = (y1 == 0 && a_axis->label_depth > d->label_depth
333                            ? dim_col_vert
334                            : cat_col_vert);
335                       draw_line (t, borders, style, b, x1 + a_ofs, y1,
336                                  t->n[b] - 1);
337                       vrules[x1] = true;
338                     }
339                 }
340               if (c->parent && c->parent->show_label)
341                 draw_line (t, borders, cat_col_horz, a, y1,
342                            x1 + a_ofs, x2 + a_ofs - 1);
343               x1 = x2;
344             }
345         }
346
347       if (d->root->show_label_in_corner && a_ofs > 0)
348         {
349           int bb[TABLE_N_AXES][2];
350           bb[a][0] = 0;
351           bb[a][1] = a_ofs - 1;
352           bb[b][0] = top_row;
353           bb[b][1] = top_row + d->label_depth - 1;
354           fill_cell (t, bb[H][0], bb[V][0], bb[H][1], bb[V][1],
355                      corner_style, PIVOT_AREA_CORNER, d->root->name, footnotes,
356                      show_values, show_variables, false);
357         }
358
359       /* Draw the horizontal line between dimensions, e.g. the ===== line here:
360
361          +-----------------------------------------------------+ __
362          |                         bbbb                        |  |
363          +-----------------+-----------------+-----------------+  |dim "bbbb"
364          |      bbbb1      |      bbbb2      |      bbbb3      | _|
365          +=================+=================+=================+ __
366          |       aaaa      |       aaaa      |       aaaa      |  |
367          +-----+-----+-----+-----+-----+-----+-----+-----+-----+  |dim "aaaa"
368          |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3| _|
369          +-----+-----+-----+-----+-----+-----+-----+-----+-----+
370       */
371       if (dim_index != a_axis->n_dimensions - 1)
372         draw_line (t, borders, dim_col_horz, a, top_row, a_ofs,
373                    t->n[a] - 1);
374       top_row += d->label_depth;
375     }
376   free (vrules);
377 }
378
379 static void
380 pivot_table_submit_layer (const struct pivot_table *pt,
381                           const size_t *layer_indexes)
382 {
383   const size_t *pindexes[PIVOT_N_AXES]
384     = { [PIVOT_AXIS_LAYER] = layer_indexes };
385
386   size_t body[TABLE_N_AXES];
387   size_t *column_enumeration = pivot_table_enumerate_axis (
388     pt, PIVOT_AXIS_COLUMN, layer_indexes, pt->look->omit_empty, &body[H]);
389   size_t *row_enumeration = pivot_table_enumerate_axis (
390     pt, PIVOT_AXIS_ROW, layer_indexes, pt->look->omit_empty, &body[V]);
391
392   int stub[TABLE_N_AXES] = {
393     [H] = pt->axes[PIVOT_AXIS_ROW].label_depth,
394     [V] = pt->axes[PIVOT_AXIS_COLUMN].label_depth,
395   };
396   struct table *table = table_create (body[H] + stub[H],
397                                       body[V] + stub[V],
398                                       stub[H], 0, stub[V], 0);
399
400   for (size_t i = 0; i < PIVOT_N_AREAS; i++)
401     table->styles[i] = table_area_style_override (
402       table->container, &pt->look->areas[i], NULL, NULL, false);
403
404   for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
405     {
406       const struct table_border_style *in = &pt->look->borders[i];
407       table->rule_colors[i] = pool_alloc (table->container,
408                                           sizeof *table->rule_colors[i]);
409       struct cell_color *out = table->rule_colors[i];
410       out->alpha = in->color.alpha;
411       out->r = in->color.r;
412       out->g = in->color.g;
413       out->b = in->color.b;
414     }
415
416   struct footnote **footnotes = XCALLOC (pt->n_footnotes,  struct footnote *);
417   for (size_t i = 0; i < pt->n_footnotes; i++)
418     {
419       const struct pivot_footnote *pf = pt->footnotes[i];
420
421       if (!pf->show)
422         continue;
423
424       char *content = pivot_value_to_string (pf->content, pt->show_values,
425                                              pt->show_variables);
426       char *marker = pivot_value_to_string (pf->marker, pt->show_values,
427                                             pt->show_variables);
428       footnotes[i] = table_create_footnote (
429         table, i, content, marker,
430         table_area_style_override (table->container,
431                                    &pt->look->areas[PIVOT_AREA_FOOTER],
432                                    pf->content->cell_style,
433                                    pf->content->font_style,
434                                    false));
435       free (marker);
436       free (content);
437     }
438
439   compose_headings (table,
440                     &pt->axes[PIVOT_AXIS_COLUMN], H, &pt->axes[PIVOT_AXIS_ROW],
441                     pt->look->borders,
442                     PIVOT_BORDER_DIM_COL_HORZ,
443                     PIVOT_BORDER_DIM_COL_VERT,
444                     PIVOT_BORDER_CAT_COL_HORZ,
445                     PIVOT_BORDER_CAT_COL_VERT,
446                     column_enumeration, body[H],
447                     &pt->look->areas[PIVOT_AREA_COLUMN_LABELS],
448                     PIVOT_AREA_COLUMN_LABELS,
449                     &pt->look->areas[PIVOT_AREA_CORNER], footnotes,
450                     pt->show_values, pt->show_variables,
451                     pt->rotate_outer_row_labels, false);
452
453   compose_headings (table,
454                     &pt->axes[PIVOT_AXIS_ROW], V, &pt->axes[PIVOT_AXIS_COLUMN],
455                     pt->look->borders,
456                     PIVOT_BORDER_DIM_ROW_VERT,
457                     PIVOT_BORDER_DIM_ROW_HORZ,
458                     PIVOT_BORDER_CAT_ROW_VERT,
459                     PIVOT_BORDER_CAT_ROW_HORZ,
460                     row_enumeration, body[V],
461                     &pt->look->areas[PIVOT_AREA_ROW_LABELS],
462                     PIVOT_AREA_ROW_LABELS,
463                     &pt->look->areas[PIVOT_AREA_CORNER], footnotes,
464                     pt->show_values, pt->show_variables,
465                     false, pt->rotate_inner_column_labels);
466
467   size_t *dindexes = XCALLOC (pt->n_dimensions, size_t);
468   size_t y = 0;
469   PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
470                               &pt->axes[PIVOT_AXIS_ROW])
471     {
472       size_t x = 0;
473       PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
474                                   column_enumeration,
475                                   &pt->axes[PIVOT_AXIS_COLUMN])
476         {
477           pivot_table_convert_indexes_ptod (pt, pindexes, dindexes);
478           const struct pivot_value *value = pivot_table_get (pt, dindexes);
479           fill_cell (table,
480                      x + stub[H], y + stub[V],
481                      x + stub[H], y + stub[V],
482                      &pt->look->areas[PIVOT_AREA_DATA], PIVOT_AREA_DATA,
483                      value, footnotes,
484                      pt->show_values, pt->show_variables, false);
485
486           x++;
487         }
488
489       y++;
490     }
491   free (dindexes);
492
493   if ((pt->corner_text || !pt->look->row_labels_in_corner)
494       && stub[H] && stub[V])
495     fill_cell (table, 0, 0, stub[H] - 1, stub[V] - 1,
496                &pt->look->areas[PIVOT_AREA_CORNER], PIVOT_AREA_CORNER,
497                pt->corner_text, footnotes,
498                pt->show_values, pt->show_variables, false);
499
500   if (table->n[H] && table->n[V])
501     {
502       table_hline (
503         table, get_table_rule (pt->look->borders, PIVOT_BORDER_INNER_TOP),
504         0, table->n[H] - 1, 0);
505       table_hline (
506         table, get_table_rule (pt->look->borders, PIVOT_BORDER_INNER_BOTTOM),
507         0, table->n[H] - 1, table->n[V]);
508       table_vline (
509         table, get_table_rule (pt->look->borders, PIVOT_BORDER_INNER_LEFT),
510         0, 0, table->n[V] - 1);
511       table_vline (
512         table, get_table_rule (pt->look->borders, PIVOT_BORDER_INNER_RIGHT),
513         table->n[H], 0, table->n[V] - 1);
514
515       if (stub[V])
516         table_hline (
517           table, get_table_rule (pt->look->borders, PIVOT_BORDER_DATA_TOP),
518           0, table->n[H] - 1, stub[V]);
519       if (stub[H])
520         table_vline (
521           table, get_table_rule (pt->look->borders, PIVOT_BORDER_DATA_LEFT),
522           stub[H], 0, table->n[V] - 1);
523
524     }
525   free (column_enumeration);
526   free (row_enumeration);
527
528   struct table_item *ti = table_item_create (table, NULL, NULL, pt->notes);
529
530   if (pt->title && pt->show_title)
531     {
532       struct table_item_text *title = pivot_value_to_table_item_text (
533         pt->title, &pt->look->areas[PIVOT_AREA_TITLE], footnotes,
534         pt->show_values, pt->show_variables);
535       table_item_set_title (ti, title);
536       table_item_text_destroy (title);
537     }
538
539   const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
540   struct table_item_layers *layers = NULL;
541   for (size_t i = 0; i < layer_axis->n_dimensions; i++)
542     {
543       const struct pivot_dimension *d = layer_axis->dimensions[i];
544       if (d->n_leaves)
545         {
546           if (!layers)
547             {
548               layers = xzalloc (sizeof *layers);
549               layers->style = table_area_style_override (
550                 NULL, &pt->look->areas[PIVOT_AREA_LAYERS], NULL, NULL, false);
551               layers->layers = xnmalloc (layer_axis->n_dimensions,
552                                          sizeof *layers->layers);
553             }
554
555           const struct pivot_value *name
556             = d->data_leaves[layer_indexes[i]]->name;
557           struct table_item_layer *layer = &layers->layers[layers->n_layers++];
558           struct string s = DS_EMPTY_INITIALIZER;
559           pivot_value_format_body (name, pt->show_values, pt->show_variables,
560                                    &s);
561           layer->content = ds_steal_cstr (&s);
562           layer->n_footnotes = 0;
563           layer->footnotes = xnmalloc (name->n_footnotes,
564                                        sizeof *layer->footnotes);
565           for (size_t i = 0; i < name->n_footnotes; i++)
566             {
567               struct footnote *f = footnotes[name->footnotes[i]->idx];
568               if (f)
569                 layer->footnotes[layer->n_footnotes++] = f;
570             }
571         }
572     }
573   if (layers)
574     {
575       table_item_set_layers (ti, layers);
576       table_item_layers_destroy (layers);
577     }
578
579   if (pt->caption && pt->show_caption)
580     {
581       struct table_item_text *caption = pivot_value_to_table_item_text (
582         pt->caption, &pt->look->areas[PIVOT_AREA_CAPTION], footnotes,
583         pt->show_values, pt->show_variables);
584       table_item_set_caption (ti, caption);
585       table_item_text_destroy (caption);
586     }
587
588   free (footnotes);
589   ti->pt = pivot_table_ref (pt);
590
591   table_item_submit (ti);
592 }
593
594 void
595 pivot_table_submit (struct pivot_table *pt)
596 {
597   pivot_table_assign_label_depth (CONST_CAST (struct pivot_table *, pt));
598
599   int old_decimal = settings_get_decimal_char (FMT_COMMA);
600   if (pt->decimal == '.' || pt->decimal == ',')
601     settings_set_decimal_char (pt->decimal);
602
603   if (pt->look->print_all_layers)
604     {
605       size_t *layer_indexes;
606
607       PIVOT_AXIS_FOR_EACH (layer_indexes, &pt->axes[PIVOT_AXIS_LAYER])
608         {
609           if (pt->look->paginate_layers)
610             page_eject_item_submit (page_eject_item_create ());
611           pivot_table_submit_layer (pt, layer_indexes);
612         }
613     }
614   else
615     pivot_table_submit_layer (pt, pt->current_layer);
616
617   settings_set_decimal_char (old_decimal);
618
619   pivot_table_unref (pt);
620 }