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