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