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