output: Render pivot tables monolithically.
[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 x, int y,
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, x, y, x, y, options, value);
124 }
125
126 static void
127 fill_cell_spanned (struct table *t, int x1, int y1, int x2, int y2,
128                    int style_idx, const struct pivot_value *value,
129                    bool rotate_label, int options)
130 {
131   options |= style_idx << TABLE_CELL_STYLE_SHIFT;
132   if (rotate_label)
133     options |= TABLE_CELL_ROTATE;
134
135   table_put (t, x1, y1, x2, y2, options, value);
136 }
137
138 static void
139 fill_cell_owned (struct table *t, int x1, int y1, int x2, int y2,
140                  int style_idx, struct string *s, bool rotate_label,
141                  int options)
142 {
143   options |= style_idx << TABLE_CELL_STYLE_SHIFT;
144   if (rotate_label)
145     options |= TABLE_CELL_ROTATE;
146
147   table_put_owned (t, x1, y1, x2, y2, options,
148                    pivot_value_new_user_text_nocopy (ds_steal_cstr (s)));
149 }
150
151 static void
152 draw_line (struct table *t, enum pivot_border border_idx,
153            enum table_axis axis, int a, int b0, int b1)
154 {
155   if (axis == H)
156     table_hline (t, border_idx, b0, b1, a);
157   else
158     table_vline (t, border_idx, a, b0, b1);
159 }
160
161 /* Fills row or column headings into T.
162
163    This function uses terminology and variable names for column headings, but
164    it also applies to row headings because it uses variables for the
165    differences, e.g. when for column headings it would use the H axis, it
166    instead uses 'h', which is set to H for column headings and V for row
167    headings.  */
168 static void
169 compose_headings (struct table *t,
170                   const struct pivot_axis *h_axis, enum table_axis h,
171                   const struct pivot_axis *v_axis,
172                   enum pivot_border dim_col_horz,
173                   enum pivot_border dim_col_vert,
174                   enum pivot_border cat_col_horz,
175                   enum pivot_border cat_col_vert,
176                   const size_t *column_enumeration, size_t n_columns,
177                   int label_style_idx,
178                   bool rotate_inner_labels, bool rotate_outer_labels,
179                   int area[TABLE_N_AXES][2])
180 {
181   const enum table_axis v = !h;
182   const int v_size = h_axis->label_depth;
183   const int h_ofs = v_axis->label_depth + area[h][0];
184
185   if (!h_axis->n_dimensions || !n_columns || !v_size)
186     return;
187
188   const int stride = MAX (1, h_axis->n_dimensions);
189
190   /* Below, we're going to iterate through the dimensions.  Each dimension
191      occupies one or more rows in the heading.  'top_row' is the top row of
192      these (and 'top_row + d->label_depth - 1' is the bottom row). */
193   int top_row = area[v][0];
194
195   const int h_max = area[h][1];
196   const int v_max = area[v][1];
197
198   /* We're going to iterate through dimensions and the rows that label them
199      from top to bottom (from outer to inner dimensions).  As we move downward,
200      we start drawing vertical rules to separate categories and groups.  After
201      we start drawing a vertical rule in a particular horizontal position, it
202      continues until the bottom of the heading.  vrules[pos] indicates whether,
203      in our current row, we have already started drawing a vertical rule in
204      horizontal position 'pos'.  (There are n_columns + 1 horizontal positions.
205      We allocate all of them for convenience below but only the inner n_columns
206      - 1 of them really matter.)
207
208      Here's an example that shows how vertical rules continue all the way
209      downward:
210
211      +-----------------------------------------------------+ __
212      |                         bbbb                        |  |
213      +-----------------+-----------------+-----------------+  |dimension "bbbb"
214      |      bbbb1      |      bbbb2      |      bbbb3      | _|
215      +-----------------+-----------------+-----------------+ __
216      |       aaaa      |       aaaa      |       aaaa      |  |
217      +-----+-----+-----+-----+-----+-----+-----+-----+-----+  |dimension "aaaa"
218      |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3| _|
219      +-----+-----+-----+-----+-----+-----+-----+-----+-----+
220
221      ^     ^     ^     ^     ^     ^     ^     ^     ^     ^
222      |     |     |     |     |     |     |     |     |     |
223      0     1     2     3     4     5     6     7     8     9
224      |___________________vrules[] indexes__________________|
225
226      Our data structures are more naturally iterated from bottom to top (inner
227      to outer dimensions).  A previous version of this code actually worked
228      like that, but it didn't draw all of the vertical lines correctly as shown
229      above.  It ended up rendering the above heading much like shown below,
230      which isn't what users expect.  The "aaaa" label really needs to be shown
231      three times for clarity:
232
233      +-----------------------------------------------------+
234      |                         bbbb                        |
235      +-----------------+-----------------+-----------------+
236      |      bbbb1      |      bbbb2      |      bbbb3      |
237      +-----------------+-----------------+-----------------+
238      |                 |       aaaa      |                 |
239      +-----+-----+-----+-----+-----+-----+-----+-----+-----+
240      |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|
241      +-----+-----+-----+-----+-----+-----+-----+-----+-----+
242   */
243   bool *vrules = XCALLOC (n_columns + 1, bool);
244   vrules[0] = vrules[n_columns] = true;
245   for (int dim_index = h_axis->n_dimensions; --dim_index >= 0; )
246     {
247       const struct pivot_dimension *d = h_axis->dimensions[dim_index];
248       if (d->hide_all_labels)
249         continue;
250
251       for (int row_ofs = 0; row_ofs < d->label_depth; row_ofs++)
252         {
253           for (size_t x1 = 0; x1 < n_columns;)
254             {
255               const struct pivot_category *c = find_category (
256                 d, dim_index, column_enumeration + x1 * stride,
257                 d->label_depth - row_ofs - 1);
258               if (!c)
259                 {
260                   x1++;
261                   continue;
262                 }
263
264               size_t x2;
265               for (x2 = x1 + 1; x2 < n_columns; x2++)
266                 {
267                   if (vrules[x2])
268                     break;
269                   const struct pivot_category *c2 = find_category (
270                     d, dim_index, column_enumeration + x2 * stride,
271                     d->label_depth - row_ofs - 1);
272                   if (c != c2)
273                     break;
274                 }
275
276               int y1 = top_row + row_ofs;
277               int y2 = top_row + row_ofs + c->extra_depth + 1;
278               if (pivot_category_is_leaf (c) || c->show_label)
279                 {
280                   int bb[TABLE_N_AXES][2];
281                   bb[h][0] = x1 + h_ofs;
282                   bb[h][1] = x2 + h_ofs - 1;
283                   bb[v][0] = y1;
284                   bb[v][1] = y2 - 1;
285                   bool is_outer_row = y1 == area[v][0];
286                   bool is_inner_row = y2 == v_size + area[v][0];
287                   bool rotate = ((rotate_inner_labels && is_inner_row)
288                                  || (rotate_outer_labels && is_outer_row));
289                   fill_cell_spanned (t, bb[H][0], bb[V][0], bb[H][1], bb[V][1],
290                                      label_style_idx, c->name, rotate, 0);
291
292                   /* Draw all the vertical lines in our running example, other
293                      than the far left and far right ones.  Only the ones that
294                      start in the last row of the heading are drawn with the
295                      "category" style, the rest with the "dimension" style,
296                      e.g. only the # below are category style:
297
298                      +-----------------------------------------------------+
299                      |                         bbbb                        |
300                      +-----------------+-----------------+-----------------+
301                      |      bbbb1      |      bbbb2      |      bbbb3      |
302                      +-----------------+-----------------+-----------------+
303                      |       aaaa      |       aaaa      |       aaaa      |
304                      +-----+-----+-----+-----+-----+-----+-----+-----+-----+
305                      |aaaa1#aaaa2#aaaa3|aaaa1#aaaa2#aaaa3|aaaa1#aaaa2#aaaa3|
306                      +-----+-----+-----+-----+-----+-----+-----+-----+-----+
307                   */
308                   enum pivot_border style
309                     = (y1 == area[v][0] + v_size - 1 ? cat_col_vert : dim_col_vert);
310                   if (!vrules[x2])
311                     {
312                       draw_line (t, style, v, x2 + h_ofs, y1, v_max);
313                       vrules[x2] = true;
314                     }
315                   if (!vrules[x1])
316                     {
317                       draw_line (t, style, v, x1 + h_ofs, y1, v_max);
318                       vrules[x1] = true;
319                     }
320                 }
321
322               /* Draws the horizontal lines within a dimension, that is, those
323                  that separate a separating a category (or group) from its
324                  parent group or dimension's label.  Our running example
325                  doesn't have groups but the ==== lines below show the
326                  separators between categories and their dimension label:
327
328                  +-----------------------------------------------------+
329                  |                         bbbb                        |
330                  +=================+=================+=================+
331                  |      bbbb1      |      bbbb2      |      bbbb3      |
332                  +-----------------+-----------------+-----------------+
333                  |       aaaa      |       aaaa      |       aaaa      |
334                  +=====+=====+=====+=====+=====+=====+=====+=====+=====+
335                  |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|
336                  +-----+-----+-----+-----+-----+-----+-----+-----+-----+
337               */
338               if (c->parent && c->parent->show_label)
339                 draw_line (t, cat_col_horz, h, y1, x1 + h_ofs, x2 + h_ofs - 1);
340               x1 = x2;
341             }
342         }
343
344       if (d->root->show_label_in_corner && h_ofs > area[h][0])
345         {
346           int bb[TABLE_N_AXES][2];
347           bb[h][0] = area[h][0];
348           bb[h][1] = h_ofs - 1;
349           bb[v][0] = top_row;
350           bb[v][1] = top_row + d->label_depth - 1;
351           fill_cell_spanned (t, bb[H][0], bb[V][0], bb[H][1], bb[V][1],
352                              PIVOT_AREA_CORNER, d->root->name, false, 0);
353         }
354
355       /* Draw the horizontal line between dimensions, e.g. the ===== line here:
356
357          +-----------------------------------------------------+ __
358          |                         bbbb                        |  |
359          +-----------------+-----------------+-----------------+  |dim "bbbb"
360          |      bbbb1      |      bbbb2      |      bbbb3      | _|
361          +=================+=================+=================+ __
362          |       aaaa      |       aaaa      |       aaaa      |  |
363          +-----+-----+-----+-----+-----+-----+-----+-----+-----+  |dim "aaaa"
364          |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3| _|
365          +-----+-----+-----+-----+-----+-----+-----+-----+-----+
366       */
367       if (dim_index != h_axis->n_dimensions - 1)
368         draw_line (t, dim_col_horz, h, top_row, h_ofs, h_max);
369       top_row += d->label_depth;
370     }
371   free (vrules);
372 }
373
374 static struct table *
375 create_aux_table (const struct pivot_table *pt, int nc, int nr,
376                   int style_idx)
377 {
378   struct table *table = table_create (nc, nr, 0, 0);
379   table->styles[style_idx] = table_area_style_override (
380       table->container, &pt->look->areas[style_idx], NULL, NULL, false);
381   return table;
382 }
383
384
385 static void
386 add_references (const struct pivot_table *pt, const struct table *table,
387                 bool *refs, size_t *n_refs)
388 {
389   if (!table)
390     return;
391
392   for (int y = 0; y < table->n[V]; y++)
393     for (int x = 0; x < table->n[H]; )
394       {
395         struct table_cell cell;
396         table_get_cell (table, x, y, &cell);
397
398         if (x == cell.d[H][0] && y == cell.d[V][0])
399           {
400             const struct pivot_value_ex *ex = pivot_value_ex (cell.value);
401             for (size_t i = 0; i < ex->n_footnotes; i++)
402               {
403                 size_t idx = ex->footnote_indexes[i];
404                 assert (idx < pt->n_footnotes);
405
406                 if (!refs[idx] && pt->footnotes[idx]->show)
407                   {
408                     refs[idx] = true;
409                     (*n_refs)++;
410                   }
411               }
412           }
413
414         x = cell.d[TABLE_HORZ][1];
415       }
416 }
417
418 static struct pivot_footnote **
419 collect_footnotes (const struct pivot_table *pt,
420                    const struct table *title,
421                    const struct table *layers,
422                    const struct table *body,
423                    const struct table *caption,
424                    size_t *n_footnotesp)
425 {
426   if (!pt->n_footnotes)
427     {
428       *n_footnotesp = 0;
429       return NULL;
430     }
431
432   bool *refs = XCALLOC (pt->n_footnotes, bool);
433   size_t n_refs = 0;
434   add_references (pt, title, refs, &n_refs);
435   add_references (pt, layers, refs, &n_refs);
436   add_references (pt, body, refs, &n_refs);
437   add_references (pt, caption, refs, &n_refs);
438
439   struct pivot_footnote **footnotes = xnmalloc (n_refs, sizeof *footnotes);
440   size_t n_footnotes = 0;
441   for (size_t i = 0; i < pt->n_footnotes; i++)
442     if (refs[i])
443       footnotes[n_footnotes++] = pt->footnotes[i];
444   assert (n_footnotes == n_refs);
445
446   free (refs);
447
448   *n_footnotesp = n_footnotes;
449   return footnotes;
450 }
451
452 static enum pivot_border
453 pivot_border_fallback (enum pivot_border border)
454 {
455   switch (border)
456     {
457     case PIVOT_BORDER_TITLE:
458     case PIVOT_BORDER_OUTER_LEFT:
459     case PIVOT_BORDER_OUTER_TOP:
460     case PIVOT_BORDER_OUTER_RIGHT:
461     case PIVOT_BORDER_OUTER_BOTTOM:
462     case PIVOT_BORDER_INNER_LEFT:
463     case PIVOT_BORDER_INNER_TOP:
464     case PIVOT_BORDER_INNER_RIGHT:
465     case PIVOT_BORDER_INNER_BOTTOM:
466     case PIVOT_BORDER_DATA_LEFT:
467     case PIVOT_BORDER_DATA_TOP:
468       return border;
469
470     /* Dimensions. */
471     case PIVOT_BORDER_DIM_ROW_HORZ:
472       return PIVOT_BORDER_CAT_ROW_HORZ;
473     case PIVOT_BORDER_DIM_ROW_VERT:
474       return PIVOT_BORDER_CAT_ROW_VERT;
475     case PIVOT_BORDER_DIM_COL_HORZ:
476       return PIVOT_BORDER_CAT_COL_HORZ;
477     case PIVOT_BORDER_DIM_COL_VERT:
478       return PIVOT_BORDER_CAT_COL_VERT;
479
480     /* Categories. */
481     case PIVOT_BORDER_CAT_ROW_HORZ:
482     case PIVOT_BORDER_CAT_ROW_VERT:
483     case PIVOT_BORDER_CAT_COL_HORZ:
484     case PIVOT_BORDER_CAT_COL_VERT:
485       return border;
486
487     case PIVOT_N_BORDERS:
488     default:
489       NOT_REACHED ();
490     }
491 }
492
493 static struct table_border_style
494 resolve_border_style (const struct pivot_table_look *look, enum pivot_border b,
495                       bool show_grid_lines)
496 {
497   struct table_border_style style = look->borders[b];
498   if (style.stroke != TABLE_STROKE_NONE)
499     return style;
500
501   style = look->borders[pivot_border_fallback (b)];
502   if (style.stroke != TABLE_STROKE_NONE)
503     return style;
504
505   if (show_grid_lines)
506     return (struct table_border_style) { .stroke = TABLE_STROKE_DASHED,
507                                          .color = CELL_COLOR_BLACK };
508
509   return style;
510 }
511
512 static struct table *
513 pivot_output_title (const struct pivot_table *pt)
514 {
515   if (!pt->title || !pt->show_title)
516     return NULL;
517   struct table *title = create_aux_table (pt, 1, 1, PIVOT_AREA_TITLE);
518   fill_cell (title, 0, 0, PIVOT_AREA_TITLE, pt->title, false);
519   return title;
520 }
521
522 static int
523 pivot_count_layers (const struct pivot_table *pt)
524 {
525   const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
526   int n_layers = 0;
527   for (size_t i = 0; i < layer_axis->n_dimensions; i++)
528     {
529       const struct pivot_dimension *d = layer_axis->dimensions[i];
530       if (d->n_leaves)
531         n_layers++;
532     }
533   return n_layers;
534 }
535
536 static void
537 put_layers (struct table *t, const struct pivot_table *pt,
538             const size_t *layer_indexes, int x1, int y1 UNUSED, int x2, int y2)
539 {
540   assert (y1 + pivot_count_layers (pt) - 1 == y2);
541   const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
542   for (size_t i = 0; i < layer_axis->n_dimensions; i++)
543     {
544       const struct pivot_dimension *d = layer_axis->dimensions[i];
545       if (!d->n_leaves)
546         continue;
547
548       struct string s = DS_EMPTY_INITIALIZER;
549       pivot_value_format (d->data_leaves[layer_indexes[i]]->name, pt, &s);
550       int y = y2 - i;
551       fill_cell_owned (t, x1, y, x2, y, PIVOT_AREA_LAYERS, &s, false,
552                        TABLE_CELL_FULL_WIDTH);
553     }
554 }
555
556 static struct table *
557 pivot_output_layers (const struct pivot_table *pt, const size_t *layer_indexes)
558 {
559   int n_layers = pivot_count_layers (pt);
560   if (!n_layers)
561     return NULL;
562
563   struct table *layers = create_aux_table (pt, 1, n_layers, PIVOT_AREA_LAYERS);
564   put_layers (layers, pt, layer_indexes, 0, 0, 0, n_layers - 1);
565   return layers;
566 }
567
568 static struct table *
569 pivot_output_caption (const struct pivot_table *pt)
570 {
571   if (!pt->caption || !pt->show_caption)
572     return NULL;
573
574   struct table *caption = create_aux_table (pt, 1, 1, PIVOT_AREA_CAPTION);
575   fill_cell (caption, 0, 0, PIVOT_AREA_CAPTION, pt->caption, false);
576   return caption;
577 }
578
579 static void
580 put_footnotes (struct table *t, const struct pivot_table *pt,
581                struct pivot_footnote **f, size_t nf,
582                int x1, int x2, int y1)
583 {
584   for (size_t i = 0; i < nf; i++)
585     {
586       struct string s = DS_EMPTY_INITIALIZER;
587       pivot_footnote_format_marker (f[i], pt, &s);
588       ds_put_cstr (&s, ". ");
589       pivot_value_format (f[i]->content, pt, &s);
590       int y = y1 + i;
591       fill_cell_owned (t, x1, y, x2, y, PIVOT_AREA_FOOTER, &s, false,
592                        TABLE_CELL_FULL_WIDTH);
593     }
594 }
595
596 static struct table *
597 pivot_output_footnotes (const struct pivot_table *pt,
598                         struct pivot_footnote **f, size_t nf)
599 {
600   if (!nf)
601     return NULL;
602
603   struct table *footnotes = create_aux_table (pt, 1, nf, PIVOT_AREA_FOOTER);
604   put_footnotes (footnotes, pt, f, nf, 0, 0, 0);
605   return footnotes;
606 }
607
608 void
609 pivot_output (const struct pivot_table *pt,
610               const size_t *layer_indexes,
611               bool printing,
612               struct table **titlep,
613               struct table **layersp,
614               struct table **bodyp,
615               struct table **captionp,
616               struct table **footnotesp,
617               struct pivot_footnote ***fp, size_t *nfp)
618 {
619   const size_t *pindexes[PIVOT_N_AXES]
620     = { [PIVOT_AXIS_LAYER] = layer_indexes };
621
622   size_t data[TABLE_N_AXES];
623   size_t *column_enumeration = pivot_table_enumerate_axis (
624     pt, PIVOT_AXIS_COLUMN, layer_indexes, pt->look->omit_empty, &data[H]);
625   size_t *row_enumeration = pivot_table_enumerate_axis (
626     pt, PIVOT_AXIS_ROW, layer_indexes, pt->look->omit_empty, &data[V]);
627
628   int stub[TABLE_N_AXES] = {
629     [H] = pt->axes[PIVOT_AXIS_ROW].label_depth,
630     [V] = pt->axes[PIVOT_AXIS_COLUMN].label_depth,
631   };
632
633   struct table *body = table_create (stub[H] + data[H], stub[V] + data[V],
634                                      stub[H], stub[V]);
635   for (size_t i = 0; i < PIVOT_N_AREAS; i++)
636     body->styles[i] = table_area_style_override (
637       body->container, &pt->look->areas[i], NULL, NULL, false);
638
639   body->n_borders = PIVOT_N_BORDERS;
640   body->borders = pool_nmalloc (body->container, PIVOT_N_BORDERS,
641                                 sizeof *body->borders);
642   for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
643     body->borders[i] = resolve_border_style (pt->look, i,
644                                              printing && pt->show_grid_lines);
645
646   int body_area[TABLE_N_AXES][2] = {
647     [H] = { 0, body->n[H] - 1 },
648     [V] = { 0, body->n[V] - 1 },
649   };
650   compose_headings (body,
651                     &pt->axes[PIVOT_AXIS_COLUMN], H, &pt->axes[PIVOT_AXIS_ROW],
652                     PIVOT_BORDER_DIM_COL_HORZ,
653                     PIVOT_BORDER_DIM_COL_VERT,
654                     PIVOT_BORDER_CAT_COL_HORZ,
655                     PIVOT_BORDER_CAT_COL_VERT,
656                     column_enumeration, data[H],
657                     PIVOT_AREA_COLUMN_LABELS,
658                     pt->rotate_outer_row_labels, false, body_area);
659
660   compose_headings (body,
661                     &pt->axes[PIVOT_AXIS_ROW], V, &pt->axes[PIVOT_AXIS_COLUMN],
662                     PIVOT_BORDER_DIM_ROW_VERT,
663                     PIVOT_BORDER_DIM_ROW_HORZ,
664                     PIVOT_BORDER_CAT_ROW_VERT,
665                     PIVOT_BORDER_CAT_ROW_HORZ,
666                     row_enumeration, data[V],
667                     PIVOT_AREA_ROW_LABELS,
668                     false, pt->rotate_inner_column_labels, body_area);
669
670   size_t *dindexes = XCALLOC (pt->n_dimensions, size_t);
671   size_t y = 0;
672   PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
673                               &pt->axes[PIVOT_AXIS_ROW])
674     {
675       size_t x = 0;
676       PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
677                                   column_enumeration,
678                                   &pt->axes[PIVOT_AXIS_COLUMN])
679         {
680           pivot_table_convert_indexes_ptod (pt, pindexes, dindexes);
681           const struct pivot_value *value = pivot_table_get (pt, dindexes);
682           fill_cell (body, x + stub[H], y + stub[V],
683                      PIVOT_AREA_DATA, value, false);
684
685           x++;
686         }
687
688       y++;
689     }
690   free (dindexes);
691
692   if ((pt->corner_text || !pt->look->row_labels_in_corner)
693       && stub[H] && stub[V])
694     fill_cell_spanned (body, 0, 0, stub[H] - 1, stub[V] - 1,
695                        PIVOT_AREA_CORNER, pt->corner_text, false,
696                        TABLE_CELL_FULL_WIDTH);
697
698   if (body->n[H] && body->n[V])
699     {
700       int inner[TABLE_N_AXES][2] = {
701         [H] = { 0, body->n[H] },
702         [V] = { 0, body->n[V] },
703       };
704       table_hline (body, PIVOT_BORDER_INNER_TOP,
705                    inner[H][0], inner[H][1] - 1, inner[V][0]);
706       table_hline (body, PIVOT_BORDER_INNER_BOTTOM,
707                    inner[H][0], inner[H][1] - 1, inner[V][1]);
708       table_vline (body, PIVOT_BORDER_INNER_LEFT,
709                    inner[H][0], inner[V][0], inner[V][1] - 1);
710       table_vline (body, PIVOT_BORDER_INNER_LEFT,
711                    inner[H][1], inner[V][0], inner[V][1] - 1);
712
713       if (stub[V])
714         table_hline (body, PIVOT_BORDER_DATA_TOP,
715                      inner[H][0], inner[H][1] - 1, stub[V]);
716       if (stub[H])
717         table_vline (body, PIVOT_BORDER_DATA_LEFT,
718                      stub[H], inner[V][0], inner[V][1] - 1);
719     }
720
721   free (column_enumeration);
722   free (row_enumeration);
723
724   *bodyp = body;
725   if (titlep)
726     *titlep = pivot_output_title (pt);
727   if (layersp)
728     *layersp = pivot_output_layers (pt, layer_indexes);
729   if (captionp)
730     *captionp = pivot_output_caption (pt);
731
732   if (fp || footnotesp)
733     {
734       size_t nf;
735       struct pivot_footnote **f = collect_footnotes (
736         pt, titlep ? *titlep : NULL, layersp ? *layersp : NULL, body,
737         captionp ? *captionp : NULL, &nf);
738
739       if (footnotesp)
740         *footnotesp = pivot_output_footnotes (pt, f, nf);
741
742       if (fp)
743         {
744           *fp = f;
745           *nfp = nf;
746         }
747       else
748         free (f);
749     }
750 }
751
752 struct table *
753 pivot_output_monolithic (const struct pivot_table *pt,
754                          const size_t *layer_indexes,
755                          bool printing)
756 {
757   /* Table structure:
758
759      #============ Frame ============#
760      # Title                         #
761      # Layers                        #
762      # +--- Body ------------------+ #
763      # | Stub                      | #
764      # |      +--------------------+ #
765      # |      | Data               | #
766      # |      |                    | #
767      # |      |                    | #
768      # |      |                    | #
769      # +------+--------------------+ #
770      # Caption                       #
771      # Footnotes                     #
772      #===============================#
773
774      The regions are the following size:
775
776      - Frame: Either 0 or 1 row or column wide, depending on whether the table
777        style has an outer border on each side.  The frame cells are always
778        empty and marked as TABLE_CELL_PADDING.
779
780      - Title: Either 0 or 1 row high.
781
782      - Layers: Usually, one row for each layer dimension, but less if a layer
783        dimension is empty (has no value).
784
785      - Body and data: Variable (and can be empty).
786
787      - Caption: Either 0 or 1 row high.
788
789      - Footnotes: From zero rows up to the number of footnotes in PT (only
790        footnotes referenced in the other regions are shown).
791
792      The title, layers, caption, and footnote Region join all the body's
793      columns into single cells.
794  */
795
796   /* Size of data region. */
797   size_t data[TABLE_N_AXES];
798   size_t *column_enumeration = pivot_table_enumerate_axis (
799     pt, PIVOT_AXIS_COLUMN, layer_indexes, pt->look->omit_empty, &data[H]);
800   size_t *row_enumeration = pivot_table_enumerate_axis (
801     pt, PIVOT_AXIS_ROW, layer_indexes, pt->look->omit_empty, &data[V]);
802
803   /* Size of stub region. */
804   int stub[TABLE_N_AXES] = {
805     [H] = pt->axes[PIVOT_AXIS_ROW].label_depth,
806     [V] = pt->axes[PIVOT_AXIS_COLUMN].label_depth,
807   };
808
809   int n_title = pt->title && pt->show_title;
810   int n_layers = pivot_count_layers (pt);
811   int n_caption = pt->caption && pt->show_caption;
812   int max_footnotes = pt->n_footnotes;
813
814   int min_body_width = n_title || n_layers || n_caption;
815   int n[TABLE_N_AXES] = {
816     [H] = MAX (min_body_width, stub[H] + data[H]),
817     [V] = n_title + n_layers + stub[V] + data[V] + n_caption + max_footnotes,
818   };
819
820   struct table *t = table_create (n[H], n[V],
821                                   stub[H], n_title + n_layers + stub[V]);
822   for (size_t i = 0; i < PIVOT_N_AREAS; i++)
823     t->styles[i] = table_area_style_override (
824       t->container, &pt->look->areas[i], NULL, NULL, false);
825
826   t->n_borders = PIVOT_N_BORDERS;
827   t->borders = pool_nmalloc (t->container, PIVOT_N_BORDERS, sizeof *t->borders);
828   for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
829     t->borders[i] = resolve_border_style (pt->look, i,
830                                           printing && pt->show_grid_lines);
831
832   int body[TABLE_N_AXES][2] = {
833     [H] = { 0, n[H] - 1 },
834     [V] = {
835       n_title + n_layers,
836       n_title + n_layers + stub[V] + data[V] - 1
837     },
838   };
839
840   if (n_layers)
841     put_layers (t, pt, layer_indexes,
842                 body[H][0], n_title,
843                 body[H][1], n_title + n_layers - 1);
844
845   compose_headings (t,
846                     &pt->axes[PIVOT_AXIS_COLUMN], H, &pt->axes[PIVOT_AXIS_ROW],
847                     PIVOT_BORDER_DIM_COL_HORZ,
848                     PIVOT_BORDER_DIM_COL_VERT,
849                     PIVOT_BORDER_CAT_COL_HORZ,
850                     PIVOT_BORDER_CAT_COL_VERT,
851                     column_enumeration, data[H],
852                     PIVOT_AREA_COLUMN_LABELS,
853                     pt->rotate_outer_row_labels, false, body);
854
855   compose_headings (t,
856                     &pt->axes[PIVOT_AXIS_ROW], V, &pt->axes[PIVOT_AXIS_COLUMN],
857                     PIVOT_BORDER_DIM_ROW_VERT,
858                     PIVOT_BORDER_DIM_ROW_HORZ,
859                     PIVOT_BORDER_CAT_ROW_VERT,
860                     PIVOT_BORDER_CAT_ROW_HORZ,
861                     row_enumeration, data[V],
862                     PIVOT_AREA_ROW_LABELS,
863                     false, pt->rotate_inner_column_labels, body);
864
865   int data_ofs[TABLE_N_AXES] = {
866     [H] = body[H][0] + stub[H],
867     [V] = body[V][0] + stub[V],
868   };
869
870   const size_t *pindexes[PIVOT_N_AXES] = { [PIVOT_AXIS_LAYER] = layer_indexes };
871   size_t *dindexes = XCALLOC (pt->n_dimensions, size_t);
872   int y = data_ofs[V];
873   PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
874                               &pt->axes[PIVOT_AXIS_ROW])
875     {
876       int x = data_ofs[H];
877       PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
878                                   column_enumeration,
879                                   &pt->axes[PIVOT_AXIS_COLUMN])
880         {
881           pivot_table_convert_indexes_ptod (pt, pindexes, dindexes);
882           const struct pivot_value *value = pivot_table_get (pt, dindexes);
883           fill_cell (t, x, y, PIVOT_AREA_DATA, value, false);
884           x++;
885         }
886
887       y++;
888     }
889   free (dindexes);
890
891   if ((pt->corner_text || !pt->look->row_labels_in_corner)
892       && stub[H] && stub[V])
893     fill_cell_spanned (t, body[H][0], body[V][0],
894                        body[H][0] + stub[H] - 1, body[V][0] + stub[V] - 1,
895                        PIVOT_AREA_CORNER, pt->corner_text, false, 0);
896
897   if (body[H][1] >= body[H][0] && body[V][1] >= body[V][0])
898     {
899       table_hline (t, PIVOT_BORDER_INNER_TOP,
900                    body[H][0], body[H][1], body[V][0]);
901       table_hline (t, PIVOT_BORDER_INNER_BOTTOM,
902                    body[H][0], body[H][1], body[V][1] + 1);
903       table_vline (t, PIVOT_BORDER_INNER_LEFT,
904                    body[H][0], body[V][0], body[V][1]);
905       table_vline (t, PIVOT_BORDER_INNER_RIGHT,
906                    body[H][1] + 1, body[V][0], body[V][1]);
907
908       if (stub[V])
909         table_hline (t, PIVOT_BORDER_DATA_TOP,
910                      body[H][0], body[H][1], data_ofs[V]);
911       if (stub[H])
912         table_vline (t, PIVOT_BORDER_DATA_LEFT,
913                      data_ofs[H], body[V][0], body[V][1]);
914     }
915
916   if (n_caption)
917     fill_cell_spanned (t,
918                        body[H][0], body[V][1] + 1,
919                        body[H][1], body[V][1] + 1,
920                        PIVOT_AREA_CAPTION, pt->caption, false,
921                        TABLE_CELL_FULL_WIDTH);
922
923   size_t nf;
924   struct pivot_footnote **f = collect_footnotes (pt, t, NULL, NULL, NULL, &nf);
925   assert (nf <= max_footnotes);
926   put_footnotes (t, pt, f, nf, body[H][0], body[H][1],
927                  body[V][1] + 1 + n_caption);
928   t->n[V] = body[V][1] + 1 + n_caption + nf;
929   free (f);
930
931   if (n_title)
932     {
933 #if 0
934       printf ("%d,%d - %d,%d\n", 
935               body[H][0], 0,
936               body[H][1], 0);
937 #endif
938     fill_cell_spanned (t,
939                        body[H][0], 0,
940                        body[H][1], 0,
941                        PIVOT_AREA_TITLE, pt->title, false,
942                        TABLE_CELL_FULL_WIDTH);
943     }
944
945   free (column_enumeration);
946   free (row_enumeration);
947
948 #if 0
949   printf ("output page:\n");
950   for (int y = 0; y < t->n[V]; y++)
951     for (int x = 0; x < t->n[H]; x++)
952       {
953         struct table_cell cell;
954
955         table_get_cell (t, x, y, &cell);
956         char *s = pivot_value_to_string (cell.value, NULL);
957         printf ("%d,%d - %d,%d: %s\n",
958                 cell.d[H][0], cell.d[V][0],
959                 cell.d[H][1], cell.d[V][1],
960                 s);
961         free (s);
962       }
963 #endif
964
965   return t;
966 }
967
968 void
969 pivot_table_submit (struct pivot_table *pt)
970 {
971   output_item_submit (table_item_create (pt));
972 }