monolithic rendering fixes
[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 == 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 struct table *
453 pivot_output_title (const struct pivot_table *pt)
454 {
455   if (!pt->title || !pt->show_title)
456     return NULL;
457   struct table *title = create_aux_table (pt, 1, 1, PIVOT_AREA_TITLE);
458   fill_cell (title, 0, 0, PIVOT_AREA_TITLE, pt->title, false);
459   return title;
460 }
461
462 static int
463 pivot_count_layers (const struct pivot_table *pt)
464 {
465   const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
466   int n_layers = 0;
467   for (size_t i = 0; i < layer_axis->n_dimensions; i++)
468     {
469       const struct pivot_dimension *d = layer_axis->dimensions[i];
470       if (d->n_leaves)
471         n_layers++;
472     }
473   return n_layers;
474 }
475
476 static void
477 put_layers (struct table *t, const struct pivot_table *pt,
478             const size_t *layer_indexes, int x1, int y1 UNUSED, int x2, int y2)
479 {
480   assert (y1 + pivot_count_layers (pt) - 1 == y2);
481   const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
482   for (size_t i = 0; i < layer_axis->n_dimensions; i++)
483     {
484       const struct pivot_dimension *d = layer_axis->dimensions[i];
485       if (!d->n_leaves)
486         continue;
487
488       struct string s = DS_EMPTY_INITIALIZER;
489       pivot_value_format (d->data_leaves[layer_indexes[i]]->name, pt, &s);
490       int y = y2 - i;
491       fill_cell_owned (t, x1, y, x2, y, PIVOT_AREA_LAYERS, &s, false,
492                        TABLE_CELL_FULL_WIDTH);
493     }
494 }
495
496 static struct table *
497 pivot_output_layers (const struct pivot_table *pt, const size_t *layer_indexes)
498 {
499   int n_layers = pivot_count_layers (pt);
500   if (!n_layers)
501     return NULL;
502
503   struct table *layers = create_aux_table (pt, 1, n_layers, PIVOT_AREA_LAYERS);
504   put_layers (layers, pt, layer_indexes, 0, 0, 0, n_layers - 1);
505   return layers;
506 }
507
508 static struct table *
509 pivot_output_caption (const struct pivot_table *pt)
510 {
511   if (!pt->caption || !pt->show_caption)
512     return NULL;
513
514   struct table *caption = create_aux_table (pt, 1, 1, PIVOT_AREA_CAPTION);
515   fill_cell (caption, 0, 0, PIVOT_AREA_CAPTION, pt->caption, false);
516   return caption;
517 }
518
519 static void
520 put_footnotes (struct table *t, const struct pivot_table *pt,
521                struct pivot_footnote **f, size_t nf,
522                int x1, int x2, int y1)
523 {
524   for (size_t i = 0; i < nf; i++)
525     {
526       struct string s = DS_EMPTY_INITIALIZER;
527       pivot_footnote_format_marker (f[i], pt, &s);
528       ds_put_cstr (&s, ". ");
529       pivot_value_format (f[i]->content, pt, &s);
530       int y = y1 + i;
531       fill_cell_owned (t, x1, y, x2, y, PIVOT_AREA_FOOTER, &s, false,
532                        TABLE_CELL_FULL_WIDTH);
533     }
534 }
535
536 static struct table *
537 pivot_output_footnotes (const struct pivot_table *pt,
538                         struct pivot_footnote **f, size_t nf)
539 {
540   if (!nf)
541     return NULL;
542
543   struct table *footnotes = create_aux_table (pt, 1, nf, PIVOT_AREA_FOOTER);
544   put_footnotes (footnotes, pt, f, nf, 0, 0, 0);
545   return footnotes;
546 }
547
548 void
549 pivot_output (const struct pivot_table *pt,
550               const size_t *layer_indexes,
551               bool printing,
552               struct table **titlep,
553               struct table **layersp,
554               struct table **bodyp,
555               struct table **captionp,
556               struct table **footnotesp,
557               struct pivot_footnote ***fp, size_t *nfp)
558 {
559   const size_t *pindexes[PIVOT_N_AXES]
560     = { [PIVOT_AXIS_LAYER] = layer_indexes };
561
562   size_t data[TABLE_N_AXES];
563   size_t *column_enumeration = pivot_table_enumerate_axis (
564     pt, PIVOT_AXIS_COLUMN, layer_indexes, pt->look->omit_empty, &data[H]);
565   size_t *row_enumeration = pivot_table_enumerate_axis (
566     pt, PIVOT_AXIS_ROW, layer_indexes, pt->look->omit_empty, &data[V]);
567
568   int stub[TABLE_N_AXES] = {
569     [H] = pt->axes[PIVOT_AXIS_ROW].label_depth,
570     [V] = pt->axes[PIVOT_AXIS_COLUMN].label_depth,
571   };
572
573   struct table *body = table_create (stub[H] + data[H], stub[V] + data[V],
574                                      stub[H], stub[V]);
575   for (size_t i = 0; i < PIVOT_N_AREAS; i++)
576     body->styles[i] = table_area_style_override (
577       body->container, &pt->look->areas[i], NULL, NULL, false);
578
579   body->n_borders = PIVOT_N_BORDERS;
580   body->borders = pool_nmalloc (body->container, PIVOT_N_BORDERS,
581                                 sizeof *body->borders);
582   for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
583     {
584       const struct table_border_style *src = &pt->look->borders[i];
585       struct table_border_style *dst = &body->borders[i];
586       *dst = (!printing && pt->show_grid_lines && src->stroke == TABLE_STROKE_NONE
587               ? (struct table_border_style) { .stroke = TABLE_STROKE_DASHED,
588                                               .color = CELL_COLOR_BLACK }
589               : *src);
590     }
591
592   int body_area[TABLE_N_AXES][2] = {
593     [H] = { 0, body->n[H] - 1 },
594     [V] = { 0, body->n[V] - 1 },
595   };
596   compose_headings (body,
597                     &pt->axes[PIVOT_AXIS_COLUMN], H, &pt->axes[PIVOT_AXIS_ROW],
598                     PIVOT_BORDER_DIM_COL_HORZ,
599                     PIVOT_BORDER_DIM_COL_VERT,
600                     PIVOT_BORDER_CAT_COL_HORZ,
601                     PIVOT_BORDER_CAT_COL_VERT,
602                     column_enumeration, data[H],
603                     PIVOT_AREA_COLUMN_LABELS,
604                     pt->rotate_outer_row_labels, false, body_area);
605
606   compose_headings (body,
607                     &pt->axes[PIVOT_AXIS_ROW], V, &pt->axes[PIVOT_AXIS_COLUMN],
608                     PIVOT_BORDER_DIM_ROW_VERT,
609                     PIVOT_BORDER_DIM_ROW_HORZ,
610                     PIVOT_BORDER_CAT_ROW_VERT,
611                     PIVOT_BORDER_CAT_ROW_HORZ,
612                     row_enumeration, data[V],
613                     PIVOT_AREA_ROW_LABELS,
614                     false, pt->rotate_inner_column_labels, body_area);
615
616   size_t *dindexes = XCALLOC (pt->n_dimensions, size_t);
617   size_t y = 0;
618   PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
619                               &pt->axes[PIVOT_AXIS_ROW])
620     {
621       size_t x = 0;
622       PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
623                                   column_enumeration,
624                                   &pt->axes[PIVOT_AXIS_COLUMN])
625         {
626           pivot_table_convert_indexes_ptod (pt, pindexes, dindexes);
627           const struct pivot_value *value = pivot_table_get (pt, dindexes);
628           fill_cell (body, x + stub[H], y + stub[V],
629                      PIVOT_AREA_DATA, value, false);
630
631           x++;
632         }
633
634       y++;
635     }
636   free (dindexes);
637
638   if ((pt->corner_text || !pt->look->row_labels_in_corner)
639       && stub[H] && stub[V])
640     fill_cell_spanned (body, 0, 0, stub[H] - 1, stub[V] - 1,
641                        PIVOT_AREA_CORNER, pt->corner_text, false,
642                        TABLE_CELL_FULL_WIDTH);
643
644   if (body->n[H] && body->n[V])
645     {
646       int inner[TABLE_N_AXES][2] = {
647         [H] = { 0, body->n[H] },
648         [V] = { 0, body->n[V] },
649       };
650       table_hline (body, PIVOT_BORDER_INNER_TOP,
651                    inner[H][0], inner[H][1] - 1, inner[V][0]);
652       table_hline (body, PIVOT_BORDER_INNER_BOTTOM,
653                    inner[H][0], inner[H][1] - 1, inner[V][1]);
654       table_vline (body, PIVOT_BORDER_INNER_LEFT,
655                    inner[H][0], inner[V][0], inner[V][1] - 1);
656       table_vline (body, PIVOT_BORDER_INNER_LEFT,
657                    inner[H][1], inner[V][0], inner[V][1] - 1);
658
659       if (stub[V])
660         table_hline (body, PIVOT_BORDER_DATA_TOP,
661                      inner[H][0], inner[H][1] - 1, stub[V]);
662       if (stub[H])
663         table_vline (body, PIVOT_BORDER_DATA_LEFT,
664                      stub[H], inner[V][0], inner[V][1] - 1);
665     }
666
667   free (column_enumeration);
668   free (row_enumeration);
669
670   *bodyp = body;
671   if (titlep)
672     *titlep = pivot_output_title (pt);
673   if (layersp)
674     *layersp = pivot_output_layers (pt, layer_indexes);
675   if (captionp)
676     *captionp = pivot_output_caption (pt);
677
678   if (fp || footnotesp)
679     {
680       size_t nf;
681       struct pivot_footnote **f = collect_footnotes (
682         pt, titlep ? *titlep : NULL, layersp ? *layersp : NULL, body,
683         captionp ? *captionp : NULL, &nf);
684
685       if (footnotesp)
686         *footnotesp = pivot_output_footnotes (pt, f, nf);
687
688       if (fp)
689         {
690           *fp = f;
691           *nfp = nf;
692         }
693       else
694         free (f);
695     }
696 }
697
698 struct table *
699 pivot_output_monolithic (const struct pivot_table *pt,
700                          const size_t *layer_indexes,
701                          bool printing)
702 {
703   /* Table structure:
704
705      #============ Frame ============#
706      # Title                         #
707      # Layers                        #
708      # +--- Body ------------------+ #
709      # | Stub                      | #
710      # |      +--------------------+ #
711      # |      | Data               | #
712      # |      |                    | #
713      # |      |                    | #
714      # |      |                    | #
715      # +------+--------------------+ #
716      # Caption                       #
717      # Footnotes                     #
718      #===============================#
719
720      The regions are the following size:
721
722      - Frame: Either 0 or 1 row or column wide, depending on whether the table
723        style has an outer border on each side.  The frame cells are always
724        empty and marked as TABLE_CELL_PADDING.
725
726      - Title: Either 0 or 1 row high.
727
728      - Layers: Usually, one row for each layer dimension, but less if a layer
729        dimension is empty (has no value).
730
731      - Body and data: Variable (and can be empty).
732
733      - Caption: Either 0 or 1 row high.
734
735      - Footnotes: From zero rows up to the number of footnotes in PT (only
736        footnotes referenced in the other regions are shown).
737
738      The title, layers, caption, and footnote Region join all the body's
739      columns into single cells.
740  */
741
742   /* Size of data region. */
743   size_t data[TABLE_N_AXES];
744   size_t *column_enumeration = pivot_table_enumerate_axis (
745     pt, PIVOT_AXIS_COLUMN, layer_indexes, pt->look->omit_empty, &data[H]);
746   size_t *row_enumeration = pivot_table_enumerate_axis (
747     pt, PIVOT_AXIS_ROW, layer_indexes, pt->look->omit_empty, &data[V]);
748
749   /* Size of stub region. */
750   int stub[TABLE_N_AXES] = {
751     [H] = pt->axes[PIVOT_AXIS_ROW].label_depth,
752     [V] = pt->axes[PIVOT_AXIS_COLUMN].label_depth,
753   };
754
755   int n_title = pt->title && pt->show_title;
756   int n_layers = pivot_count_layers (pt);
757   int n_caption = pt->caption && pt->show_caption;
758   int max_footnotes = pt->n_footnotes;
759
760   int min_body_width = n_title || n_layers || n_caption;
761   int n[TABLE_N_AXES] = {
762     [H] = MAX (min_body_width, stub[H] + data[H]),
763     [V] = n_title + n_layers + stub[V] + data[V] + n_caption + max_footnotes,
764   };
765
766   struct table *t = table_create (n[H], n[V],
767                                   stub[H], n_title + n_layers + stub[V]);
768   for (size_t i = 0; i < PIVOT_N_AREAS; i++)
769     t->styles[i] = table_area_style_override (
770       t->container, &pt->look->areas[i], NULL, NULL, false);
771
772   t->n_borders = PIVOT_N_BORDERS;
773   t->borders = pool_nmalloc (t->container, PIVOT_N_BORDERS, sizeof *t->borders);
774   for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
775     {
776       const struct table_border_style *src = &pt->look->borders[i];
777       struct table_border_style *dst = &t->borders[i];
778       *dst = (!printing && pt->show_grid_lines && src->stroke == TABLE_STROKE_NONE
779               ? (struct table_border_style) { .stroke = TABLE_STROKE_DASHED,
780                                               .color = CELL_COLOR_BLACK }
781               : *src);
782     }
783
784   int body[TABLE_N_AXES][2] = {
785     [H] = { 0, n[H] - 1 },
786     [V] = {
787       n_title + n_layers,
788       n_title + n_layers + stub[V] + data[V] - 1
789     },
790   };
791
792   if (n_layers)
793     put_layers (t, pt, layer_indexes,
794                 body[H][0], n_title,
795                 body[H][1], n_title + n_layers - 1);
796
797   compose_headings (t,
798                     &pt->axes[PIVOT_AXIS_COLUMN], H, &pt->axes[PIVOT_AXIS_ROW],
799                     PIVOT_BORDER_DIM_COL_HORZ,
800                     PIVOT_BORDER_DIM_COL_VERT,
801                     PIVOT_BORDER_CAT_COL_HORZ,
802                     PIVOT_BORDER_CAT_COL_VERT,
803                     column_enumeration, data[H],
804                     PIVOT_AREA_COLUMN_LABELS,
805                     pt->rotate_outer_row_labels, false, body);
806
807   compose_headings (t,
808                     &pt->axes[PIVOT_AXIS_ROW], V, &pt->axes[PIVOT_AXIS_COLUMN],
809                     PIVOT_BORDER_DIM_ROW_VERT,
810                     PIVOT_BORDER_DIM_ROW_HORZ,
811                     PIVOT_BORDER_CAT_ROW_VERT,
812                     PIVOT_BORDER_CAT_ROW_HORZ,
813                     row_enumeration, data[V],
814                     PIVOT_AREA_ROW_LABELS,
815                     false, pt->rotate_inner_column_labels, body);
816
817   int data_ofs[TABLE_N_AXES] = {
818     [H] = body[H][0] + stub[H],
819     [V] = body[V][0] + stub[V],
820   };
821
822   const size_t *pindexes[PIVOT_N_AXES] = { [PIVOT_AXIS_LAYER] = layer_indexes };
823   size_t *dindexes = XCALLOC (pt->n_dimensions, size_t);
824   int y = data_ofs[V];
825   PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
826                               &pt->axes[PIVOT_AXIS_ROW])
827     {
828       int x = data_ofs[H];
829       PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
830                                   column_enumeration,
831                                   &pt->axes[PIVOT_AXIS_COLUMN])
832         {
833           pivot_table_convert_indexes_ptod (pt, pindexes, dindexes);
834           const struct pivot_value *value = pivot_table_get (pt, dindexes);
835           fill_cell (t, x, y, PIVOT_AREA_DATA, value, false);
836           x++;
837         }
838
839       y++;
840     }
841   free (dindexes);
842
843   if ((pt->corner_text || !pt->look->row_labels_in_corner)
844       && stub[H] && stub[V])
845     fill_cell_spanned (t, body[H][0], body[V][0],
846                        body[H][0] + stub[H] - 1, body[V][0] + stub[V] - 1,
847                        PIVOT_AREA_CORNER, pt->corner_text, false, 0);
848
849   if (body[H][1] >= body[H][0] && body[V][1] >= body[V][0])
850     {
851       table_hline (t, PIVOT_BORDER_INNER_TOP,
852                    body[H][0], body[H][1], body[V][0]);
853       table_hline (t, PIVOT_BORDER_INNER_BOTTOM,
854                    body[H][0], body[H][1], body[V][1] + 1);
855       table_vline (t, PIVOT_BORDER_INNER_LEFT,
856                    body[H][0], body[V][0], body[V][1]);
857       table_vline (t, PIVOT_BORDER_INNER_RIGHT,
858                    body[H][1] + 1, body[V][0], body[V][1]);
859
860       if (stub[V])
861         table_hline (t, PIVOT_BORDER_DATA_TOP,
862                      body[H][0], body[H][1], data_ofs[V]);
863       if (stub[H])
864         table_vline (t, PIVOT_BORDER_DATA_LEFT,
865                      data_ofs[H], body[V][0], body[V][1]);
866     }
867
868   if (n_caption)
869     fill_cell_spanned (t,
870                        body[H][0], body[V][1] + 1,
871                        body[H][1], body[V][1] + 1,
872                        PIVOT_AREA_CAPTION, pt->caption, false,
873                        TABLE_CELL_FULL_WIDTH);
874
875   size_t nf;
876   struct pivot_footnote **f = collect_footnotes (pt, t, NULL, NULL, NULL, &nf);
877   assert (nf <= max_footnotes);
878   put_footnotes (t, pt, f, nf, body[H][0], body[H][1],
879                  body[V][1] + 1 + n_caption);
880   t->n[V] = body[V][1] + 1 + n_caption + nf;
881   free (f);
882
883   if (n_title)
884     {
885 #if 0
886       printf ("%d,%d - %d,%d\n", 
887               body[H][0], 0,
888               body[H][1], 0);
889 #endif
890     fill_cell_spanned (t,
891                        body[H][0], 0,
892                        body[H][1], 0,
893                        PIVOT_AREA_TITLE, pt->title, false,
894                        TABLE_CELL_FULL_WIDTH);
895     }
896
897   free (column_enumeration);
898   free (row_enumeration);
899
900 #if 0
901   printf ("output page:\n");
902   for (int y = 0; y < t->n[V]; y++)
903     for (int x = 0; x < t->n[H]; x++)
904       {
905         struct table_cell cell;
906
907         table_get_cell (t, x, y, &cell);
908         char *s = pivot_value_to_string (cell.value, NULL);
909         printf ("%d,%d - %d,%d: %s\n",
910                 cell.d[H][0], cell.d[V][0],
911                 cell.d[H][1], cell.d[V][1],
912                 s);
913         free (s);
914       }
915 #endif
916
917   return t;
918 }
919
920 void
921 pivot_table_submit (struct pivot_table *pt)
922 {
923   output_item_submit (table_item_create (pt));
924 }