Add support for reading and writing SPV files.
[pspp] / src / output / spv / spv-light-decoder.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2017, 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 "output/spv/spv-light-decoder.h"
20
21 #include <inttypes.h>
22 #include <limits.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "libpspp/i18n.h"
27 #include "libpspp/message.h"
28 #include "output/pivot-table.h"
29 #include "output/spv/light-binary-parser.h"
30 #include "output/spv/spv.h"
31
32 #include "gl/xalloc.h"
33 #include "gl/xsize.h"
34
35 static char *
36 to_utf8 (const char *s, const char *encoding)
37 {
38   return recode_string ("UTF-8", encoding, s, strlen (s));
39 }
40
41 static char *
42 to_utf8_if_nonempty (const char *s, const char *encoding)
43 {
44   return s && s[0] ? to_utf8 (s, encoding) : NULL;
45 }
46
47 static void
48 convert_widths (const uint32_t *in, uint32_t n, int **out, size_t *n_out)
49 {
50   if (n)
51     {
52       *n_out = n;
53       *out = xnmalloc (n, sizeof **out);
54       for (size_t i = 0; i < n; i++)
55         (*out)[i] = in[i];
56     }
57 }
58
59 static void
60 convert_breakpoints (const struct spvlb_breakpoints *in,
61                      size_t **out, size_t *n_out)
62 {
63   if (in && in->n_breaks)
64     {
65       *n_out = in->n_breaks;
66       *out = xnmalloc (in->n_breaks, sizeof *out);
67       for (size_t i = 0; i < in->n_breaks; i++)
68         (*out)[i] = in->breaks[i];
69     }
70 }
71
72 static void
73 convert_keeps (const struct spvlb_keeps *in,
74                struct pivot_keep **out, size_t *n_out)
75 {
76   if (in && in->n_keeps)
77     {
78       *n_out = in->n_keeps;
79       *out = xnmalloc (*n_out, sizeof **out);
80       for (size_t i = 0; i < *n_out; i++)
81         {
82           (*out)[i].ofs = in->keeps[i]->offset;
83           (*out)[i].n = in->keeps[i]->n;
84         }
85     }
86 }
87
88 static struct cell_color
89 decode_spvlb_color_string (const char *s, uint8_t def)
90 {
91   int r, g, b;
92   if (sscanf (s, "#%2x%2x%2x", &r, &g, &b) != 3)
93     {
94       if (*s)
95         {
96           fprintf (stderr, "bad color %s\n", s);
97           exit (1);
98         }
99       r = g = b = def;
100     }
101   return (struct cell_color) CELL_COLOR (r, g, b);
102 }
103
104 static struct cell_color
105 decode_spvlb_color_u32 (uint32_t x)
106 {
107   return (struct cell_color) { x >> 24, x >> 16, x >> 8, x };
108 }
109
110 static struct font_style *
111 decode_spvlb_font_style (const struct spvlb_font_style *in,
112                          const char *encoding)
113 {
114   if (!in)
115     return NULL;
116
117   struct font_style *out = xmalloc (sizeof *out);
118   *out = (struct font_style) {
119     .bold = in->bold,
120     .italic = in->italic,
121     .underline = in->underline,
122     .fg[0] = decode_spvlb_color_string (in->fg_color, 0x00),
123     .bg[0] = decode_spvlb_color_string (in->bg_color, 0xff),
124     .typeface = to_utf8 (in->typeface, encoding),
125     .size = in->size / 1.33,
126   };
127   out->fg[1] = out->fg[0];
128   out->bg[1] = out->bg[0];
129   return out;
130 }
131
132 static enum table_halign
133 decode_spvlb_halign (uint32_t in)
134 {
135   switch (in)
136     {
137     case 0:
138       return TABLE_HALIGN_CENTER;
139
140     case 2:
141       return TABLE_HALIGN_LEFT;
142
143     case 4:
144       return TABLE_HALIGN_RIGHT;
145
146     case 6:
147     case 61453:
148       return TABLE_HALIGN_DECIMAL;
149
150     case 0xffffffad:
151     case 64173:
152       return TABLE_HALIGN_MIXED;
153
154     default:
155       fprintf (stderr, "bad cell style halign %"PRIu32"\n", in);
156       exit (1);
157     }
158 }
159
160 static enum table_valign
161 decode_spvlb_valign (uint32_t in)
162 {
163   switch (in)
164     {
165     case 0:
166       return TABLE_VALIGN_CENTER;
167
168     case 1:
169       return TABLE_VALIGN_TOP;
170
171     case 3:
172       return TABLE_VALIGN_BOTTOM;
173
174     default:
175       fprintf (stderr, "bad cell style valign %"PRIu32"\n", in);
176       exit (1);
177     }
178 }
179
180 static struct cell_style *
181 decode_spvlb_cell_style (const struct spvlb_cell_style *in)
182 {
183   if (!in)
184     return NULL;
185
186   struct cell_style *out = xzalloc (sizeof *out);
187
188   out->halign = decode_spvlb_halign (in->halign);
189   out->valign = decode_spvlb_valign (in->valign);
190   out->decimal_offset = in->decimal_offset;
191   out->margin[TABLE_HORZ][0] = in->left_margin;
192   out->margin[TABLE_HORZ][1] = in->right_margin;
193   out->margin[TABLE_VERT][0] = in->top_margin;
194   out->margin[TABLE_VERT][1] = in->bottom_margin;
195
196   return out;
197 }
198
199 static struct pivot_value *decode_spvlb_value (const struct pivot_table *,
200                                              const struct spvlb_value *,
201                                              const char *encoding);
202
203 static void
204 decode_spvlb_argument (const struct pivot_table *table,
205                        const struct spvlb_argument *in,
206                        struct pivot_argument *out,
207                        const char *encoding)
208 {
209   if (in->value)
210     {
211       out->n = 1;
212       out->values = xmalloc (sizeof *out->values);
213       out->values[0] = decode_spvlb_value (table, in->value, encoding);
214     }
215   else
216     {
217       out->n = in->n_values;
218       out->values = xnmalloc (out->n, sizeof *out->values);
219       for (size_t i = 0; i < out->n; i++)
220         out->values[i] = decode_spvlb_value (table, in->values[i], encoding);
221     }
222 }
223
224 static enum settings_value_show
225 decode_spvlb_value_show (uint8_t in)
226 {
227   switch (in)
228     {
229     case 0: return SETTINGS_VALUE_SHOW_DEFAULT;
230     case 1: return SETTINGS_VALUE_SHOW_VALUE;
231     case 2: return SETTINGS_VALUE_SHOW_LABEL;
232     case 3: return SETTINGS_VALUE_SHOW_BOTH;
233     default:
234       fprintf (stderr, "bad value show %"PRIu8"\n", in);
235       exit (1);
236     }
237 }
238
239 static struct pivot_value *
240 decode_spvlb_value (const struct pivot_table *table,
241                     const struct spvlb_value *in,
242                     const char *encoding)
243 {
244   struct pivot_value *out = xzalloc (sizeof *out);
245   const struct spvlb_value_mod *vm;
246
247   switch (in->type)
248     {
249     case 1:
250       vm = in->type_01.value_mod;
251       out->type = PIVOT_VALUE_NUMERIC;
252       out->numeric.x = in->type_01.x;
253       out->numeric.format = spv_decode_fmt_spec (in->type_01.format);
254       break;
255
256     case 2:
257       vm = in->type_02.value_mod;
258       out->type = PIVOT_VALUE_NUMERIC;
259       out->numeric.x = in->type_02.x;
260       out->numeric.format = spv_decode_fmt_spec (in->type_02.format);
261       out->numeric.var_name = to_utf8_if_nonempty (in->type_02.var_name,
262                                                    encoding);
263       out->numeric.value_label = to_utf8_if_nonempty (in->type_02.value_label,
264                                                       encoding);
265       out->numeric.show = decode_spvlb_value_show (in->type_02.show);
266       break;
267
268     case 3:
269       vm = in->type_03.value_mod;
270       out->type = PIVOT_VALUE_TEXT;
271       out->text.local = to_utf8 (in->type_03.local, encoding);
272       out->text.c = to_utf8 (in->type_03.c, encoding);
273       out->text.id = to_utf8 (in->type_03.id, encoding);
274       out->text.user_provided = !in->type_03.fixed;
275       break;
276
277     case 4:
278       vm = in->type_04.value_mod;
279       out->type = PIVOT_VALUE_STRING;
280       out->string.s = to_utf8 (in->type_04.s, encoding);
281       out->string.var_name = to_utf8 (in->type_04.var_name, encoding);
282       out->string.value_label = to_utf8_if_nonempty (in->type_04.value_label,
283                                                      encoding);
284       out->string.show = decode_spvlb_value_show (in->type_04.show);
285       break;
286
287     case 5:
288       vm = in->type_05.value_mod;
289       out->type = PIVOT_VALUE_VARIABLE;
290       out->variable.var_name = to_utf8 (in->type_05.var_name, encoding);
291       out->variable.var_label = to_utf8_if_nonempty (in->type_05.var_label,
292                                                      encoding);
293       out->variable.show = decode_spvlb_value_show (in->type_05.show);
294       break;
295
296     case 6:
297       vm = in->type_06.value_mod;
298       out->type = PIVOT_VALUE_TEXT;
299       out->text.local = to_utf8 (in->type_06.local, encoding);
300       out->text.c = to_utf8 (in->type_06.c, encoding);
301       out->text.id = to_utf8 (in->type_06.id, encoding);
302       out->text.user_provided = false;
303       break;
304
305     case -1:
306       vm = in->type_else.value_mod;
307       out->type = PIVOT_VALUE_TEMPLATE;
308       out->template.local = to_utf8 (in->type_else.template, encoding);
309       out->template.id = out->template.local;
310       out->template.n_args = in->type_else.n_args;
311       out->template.args = xnmalloc (in->type_else.n_args,
312                                      sizeof *out->template.args);
313       for (size_t i = 0; i < out->template.n_args; i++)
314         decode_spvlb_argument (table, in->type_else.args[i],
315                                &out->template.args[i], encoding);
316       break;
317
318     default:
319       assert (0);
320     }
321
322   if (vm)
323     {
324       if (vm->subscript)
325         out->subscript = to_utf8 (vm->subscript, encoding);
326
327       if (vm->n_refs)
328         {
329           out->footnotes = xnmalloc (vm->n_refs, sizeof *out->footnotes);
330           for (size_t i = 0; i < vm->n_refs; i++)
331             {
332               uint16_t idx = vm->refs[i];
333               if (idx < table->n_footnotes)
334                 out->footnotes[out->n_footnotes++] = table->footnotes[idx];
335               else
336                 {
337                   fprintf (stderr, "bad footnote index: %"PRIu16" >= %zu\n",
338                            idx, table->n_footnotes);
339                   exit (1);
340                 }
341             }
342         }
343
344       if (vm->style_pair)
345         {
346           out->font_style = decode_spvlb_font_style (
347             vm->style_pair->font_style, encoding);
348           out->cell_style = decode_spvlb_cell_style (
349             vm->style_pair->cell_style);
350         }
351
352       if (vm->template_string
353           && vm->template_string->id
354           && vm->template_string->id[0]
355           && out->type == PIVOT_VALUE_TEMPLATE)
356         out->template.id = to_utf8 (vm->template_string->id, encoding);
357     }
358
359   return out;
360 }
361
362 static void
363 decode_spvlb_area (const struct spvlb_area *in, struct area_style *out,
364                    const char *encoding)
365 {
366   out->font_style.bold = (in->style & 1) != 0;
367   out->font_style.italic = (in->style & 2) != 0;
368   out->font_style.underline = in->underline;
369   out->font_style.fg[0] = decode_spvlb_color_string (in->fg_color, 0x00);
370   out->font_style.bg[0] = decode_spvlb_color_string (in->bg_color, 0xff);
371   out->font_style.typeface = to_utf8 (in->typeface, encoding);
372   out->font_style.size = in->size / 1.33;
373   out->font_style.fg[1] = (in->alternate
374                            ? decode_spvlb_color_string (in->alt_fg_color, 0x00)
375                            : out->font_style.fg[0]);
376   out->font_style.bg[1] = (in->alternate
377                            ? decode_spvlb_color_string (in->alt_bg_color, 0xff)
378                            : out->font_style.bg[0]);
379   assert (in->halign != 61453);
380   out->cell_style.halign = decode_spvlb_halign (in->halign);
381   out->cell_style.valign = decode_spvlb_valign (in->valign);
382
383   /* TABLE_HALIGN_DECIMAL doesn't seem to be a real halign for areas, which is
384      good because there's no way to indicate the decimal offset.  Just in
385      case: */
386   if (out->cell_style.halign == TABLE_HALIGN_DECIMAL)
387     out->cell_style.halign = TABLE_HALIGN_MIXED;
388
389   out->cell_style.margin[TABLE_HORZ][0] = in->left_margin;
390   out->cell_style.margin[TABLE_HORZ][1] = in->right_margin;
391   out->cell_style.margin[TABLE_VERT][0] = in->top_margin;
392   out->cell_style.margin[TABLE_VERT][1] = in->bottom_margin;
393 }
394
395 static void decode_spvlb_group (const struct pivot_table *,
396                                 struct spvlb_category **,
397                                 size_t n_categories,
398                                 bool show_label,
399                                 struct pivot_category *parent,
400                                 struct pivot_dimension *,
401                                 const char *encoding);
402
403 static void
404 decode_spvlb_categories (const struct pivot_table *table,
405                          struct spvlb_category **categories,
406                          size_t n_categories,
407                          struct pivot_category *parent,
408                          struct pivot_dimension *dimension,
409                          const char *encoding)
410 {
411   for (size_t i = 0; i < n_categories; i++)
412     {
413       const struct spvlb_category *in = categories[i];
414       if (in->group && in->group->merge)
415         {
416           decode_spvlb_categories (table, in->group->subcategories,
417                                    in->group->n_subcategories,
418                                    parent, dimension, encoding);
419           continue;
420         }
421
422       struct pivot_category *out = xzalloc (sizeof *out);
423       out->name = decode_spvlb_value (table, in->name, encoding);
424       out->parent = parent;
425       out->dimension = dimension;
426       if (in->group)
427         {
428           decode_spvlb_group (table, in->group->subcategories,
429                               in->group->n_subcategories,
430                               true, out, dimension, encoding);
431           out->data_index = SIZE_MAX;
432           out->presentation_index = SIZE_MAX;
433         }
434       else
435         {
436           out->data_index = in->leaf->leaf_index;
437           out->presentation_index = dimension->n_leaves;
438           dimension->n_leaves++;
439         }
440
441       if (parent->n_subs >= parent->allocated_subs)
442         parent->subs = x2nrealloc (parent->subs, &parent->allocated_subs,
443                                    sizeof *parent->subs);
444       parent->subs[parent->n_subs++] = out;
445     }
446 }
447
448 static void
449 decode_spvlb_group (const struct pivot_table *table,
450                     struct spvlb_category **categories,
451                     size_t n_categories, bool show_label,
452                     struct pivot_category *category,
453                     struct pivot_dimension *dimension,
454                     const char *encoding)
455 {
456   category->subs = xcalloc (n_categories, sizeof *category->subs);
457   category->n_subs = 0;
458   category->allocated_subs = 0;
459   category->show_label = show_label;
460
461   decode_spvlb_categories (table, categories, n_categories, category,
462                            dimension, encoding);
463 }
464
465 static void
466 fill_leaves (struct pivot_category *category,
467              struct pivot_dimension *dimension)
468 {
469   if (pivot_category_is_group (category))
470     {
471       for (size_t i = 0; i < category->n_subs; i++)
472         fill_leaves (category->subs[i], dimension);
473     }
474   else
475     {
476       if (category->data_index >= dimension->n_leaves)
477         {
478           fprintf (stderr, "leaf_index %zu >= n_leaves %zu\n",
479                    category->data_index, dimension->n_leaves);
480           exit (1);
481         }
482       if (dimension->data_leaves[category->data_index])
483         {
484           fprintf (stderr, "two leaves with data_index %zu\n",
485                    category->data_index);
486           exit (1);
487         }
488       dimension->data_leaves[category->data_index] = category;
489       dimension->presentation_leaves[category->presentation_index] = category;
490     }
491 }
492
493 static struct pivot_dimension *
494 decode_spvlb_dimension (const struct pivot_table *table,
495                         const struct spvlb_dimension *in,
496                         size_t idx, const char *encoding)
497 {
498   /* Convert most of the dimension. */
499   struct pivot_dimension *out = xzalloc (sizeof *out);
500   out->level = UINT_MAX;
501   out->top_index = idx;
502   out->hide_all_labels = in->props->hide_all_labels;
503
504   out->root = xzalloc (sizeof *out->root);
505   *out->root = (struct pivot_category) {
506     .name = decode_spvlb_value (table, in->name, encoding),
507     .dimension = out,
508     .data_index = SIZE_MAX,
509     .presentation_index = SIZE_MAX,
510   };
511   decode_spvlb_group (table, in->categories, in->n_categories,
512                       !in->props->hide_dim_label, out->root, out, encoding);
513
514   /* Allocate and fill the array of leaves now that we know how many there
515      are. */
516   out->data_leaves = xcalloc (out->n_leaves, sizeof *out->data_leaves);
517   out->presentation_leaves = xcalloc (out->n_leaves,
518                                       sizeof *out->presentation_leaves);
519   out->allocated_leaves = out->n_leaves;
520   fill_leaves (out->root, out);
521   for (size_t i = 0; i < out->n_leaves; i++)
522     {
523       assert (out->data_leaves[i] != NULL);
524       assert (out->presentation_leaves[i] != NULL);
525     }
526
527   return out;
528 }
529
530 static enum table_stroke
531 decode_spvlb_stroke (uint32_t stroke_type)
532 {
533   switch (stroke_type)
534     {
535     case 0: return TABLE_STROKE_NONE;
536     case 1: return TABLE_STROKE_SOLID;
537     case 2: return TABLE_STROKE_DASHED;
538     case 3: return TABLE_STROKE_THICK;
539     case 4: return TABLE_STROKE_THIN;
540     case 5: return TABLE_STROKE_DOUBLE;
541
542     default:
543       fprintf (stderr, "bad stroke %"PRIu32"\n", stroke_type);
544       exit (1);
545     }
546 }
547
548 static void
549 decode_spvlb_border (const struct spvlb_border *in, struct pivot_table *table)
550
551 {
552   if (in->border_type >= PIVOT_N_BORDERS)
553     {
554       fprintf (stderr, "bad border type %"PRIu32"\n", in->border_type);
555       exit (1);
556     }
557
558   struct table_border_style *out = &table->borders[in->border_type];
559   out->stroke = decode_spvlb_stroke (in->stroke_type);
560   out->color = decode_spvlb_color_u32 (in->color);
561 }
562
563 static void
564 decode_spvlb_axis (const uint32_t *dimension_indexes, size_t n_dimensions,
565                    enum pivot_axis_type axis_type, struct pivot_table *table)
566 {
567   struct pivot_axis *axis = &table->axes[axis_type];
568   axis->dimensions = xnmalloc (n_dimensions, sizeof *axis->dimensions);
569   axis->n_dimensions = n_dimensions;
570   axis->extent = 1;
571   for (size_t i = 0; i < n_dimensions; i++)
572     {
573       uint32_t idx = dimension_indexes[i];
574       if (idx >= table->n_dimensions)
575         {
576           fprintf (stderr, "bad dimension index %"PRIu32" >= %zu",
577                    idx, table->n_dimensions);
578           exit (1);
579         }
580
581       struct pivot_dimension *d = table->dimensions[idx];
582       if (d->level != UINT_MAX)
583         {
584           fprintf (stderr, "duplicate dimension %"PRIu32, idx);
585           exit (1);
586         }
587
588       axis->dimensions[i] = d;
589       d->axis_type = axis_type;
590       d->level = i;
591
592       axis->extent *= d->n_leaves;
593     }
594 }
595
596 static void
597 decode_data_index (uint64_t in, const struct pivot_table *table,
598                    size_t *out)
599 {
600   uint64_t remainder = in;
601   for (size_t i = table->n_dimensions - 1; i > 0; i--)
602     {
603       const struct pivot_dimension *d = table->dimensions[i];
604       if (d->n_leaves)
605         {
606           out[i] = remainder % d->n_leaves;
607           remainder /= d->n_leaves;
608         }
609       else
610         out[i] = 0;
611     }
612   if (remainder >= table->dimensions[0]->n_leaves)
613     {
614       fprintf (stderr, "out of range cell data index %"PRIu64, in);
615       exit (1);
616     }
617   out[0] = remainder;
618 }
619
620 static void
621 decode_spvlb_cells (struct spvlb_cell **in, size_t n_in,
622                     struct pivot_table *table, const char *encoding)
623 {
624   if (!table->n_dimensions)
625     return;
626
627   size_t *dindexes = xnmalloc (table->n_dimensions, sizeof *dindexes);
628   for (size_t i = 0; i < n_in; i++)
629     {
630       decode_data_index (in[i]->index, table, dindexes);
631       struct pivot_value *value = decode_spvlb_value (table, in[i]->value,
632                                                       encoding);
633       pivot_table_put (table, dindexes, table->n_dimensions, value);
634     }
635   free (dindexes);
636 }
637
638 static void
639 decode_spvlb_footnote (const struct spvlb_footnote *in, const char *encoding,
640                        size_t idx, struct pivot_table *table)
641 {
642   struct pivot_value *content = decode_spvlb_value (table, in->text, encoding);
643   struct pivot_value *marker = NULL;
644   if (in->marker)
645     {
646       marker = decode_spvlb_value (table, in->marker, encoding);
647       if (marker->type == PIVOT_VALUE_TEXT)
648         marker->text.user_provided = false;
649     }
650   pivot_table_create_footnote__ (table, idx, marker, content);
651 }
652
653 static void
654 decode_current_layer (uint64_t current_layer, struct pivot_table *table)
655 {
656   const struct pivot_axis *axis = &table->axes[PIVOT_AXIS_LAYER];
657   table->current_layer = xnmalloc (axis->n_dimensions,
658                                    sizeof *table->current_layer);
659
660   for (size_t i = 0; i < axis->n_dimensions; i++)
661     {
662       const struct pivot_dimension *d = axis->dimensions[i];
663       if (d->n_leaves)
664         {
665           table->current_layer[i] = current_layer % d->n_leaves;
666           current_layer /= d->n_leaves;
667         }
668       else
669         table->current_layer[i] = 0;
670     }
671   if (current_layer > 0)
672     {
673       fprintf (stderr, "out of range layer data index %"PRIu64, current_layer);
674       exit (1);
675     }
676 }
677
678 char *
679 decode_spvlb_table (const struct spvlb_table *in, struct pivot_table **outp)
680 {
681   if (in->header->version != 1 && in->header->version != 3)
682     return xasprintf ("unknown version %"PRIu32" (expected 1 or 3)",
683                       in->header->version);
684
685   struct pivot_table *out = xzalloc (sizeof *out);
686   out->ref_cnt = 1;
687   hmap_init (&out->cells);
688
689   const struct spvlb_y1 *y1 = (in->formats->x0 ? in->formats->x0->y1
690                                : in->formats->x3 ? in->formats->x3->y1
691                                : NULL);
692   const char *encoding;
693   if (y1)
694     encoding = y1->charset;
695   else
696     {
697       const char *dot = strchr (in->formats->locale, '.');
698       encoding = dot ? dot + 1 : "windows-1252";
699     }
700
701   /* Display settings. */
702   out->show_numeric_markers = !in->ts->show_alphabetic_markers;
703   out->rotate_inner_column_labels = in->header->rotate_inner_column_labels;
704   out->rotate_outer_row_labels = in->header->rotate_outer_row_labels;
705   out->row_labels_in_corner = in->ts->show_row_labels_in_corner;
706   out->show_grid_lines = in->borders->show_grid_lines;
707   out->footnote_marker_superscripts = in->ts->footnote_marker_superscripts;
708   out->omit_empty = in->ts->omit_empty;
709
710   const struct spvlb_x1 *x1 = in->formats->x1;
711   if (x1)
712     {
713       out->show_values = decode_spvlb_value_show (x1->show_values);
714       out->show_variables = decode_spvlb_value_show (x1->show_variables);
715     }
716
717   /* Column and row display settings. */
718   out->sizing[TABLE_VERT].range[0] = in->header->min_row_height;
719   out->sizing[TABLE_VERT].range[1] = in->header->max_row_height;
720   out->sizing[TABLE_HORZ].range[0] = in->header->min_col_width;
721   out->sizing[TABLE_HORZ].range[1] = in->header->max_col_width;
722
723   convert_widths (in->formats->widths, in->formats->n_widths,
724                   &out->sizing[TABLE_HORZ].widths,
725                   &out->sizing[TABLE_HORZ].n_widths);
726
727   const struct spvlb_x2 *x2 = in->formats->x2;
728   if (x2)
729     convert_widths (x2->row_heights, x2->n_row_heights,
730                     &out->sizing[TABLE_VERT].widths,
731                     &out->sizing[TABLE_VERT].n_widths);
732
733   convert_breakpoints (in->ts->row_breaks,
734                        &out->sizing[TABLE_VERT].breaks,
735                        &out->sizing[TABLE_VERT].n_breaks);
736   convert_breakpoints (in->ts->col_breaks,
737                        &out->sizing[TABLE_HORZ].breaks,
738                        &out->sizing[TABLE_HORZ].n_breaks);
739
740   convert_keeps (in->ts->row_keeps,
741                  &out->sizing[TABLE_VERT].keeps,
742                  &out->sizing[TABLE_VERT].n_keeps);
743   convert_keeps (in->ts->col_keeps,
744                  &out->sizing[TABLE_HORZ].keeps,
745                  &out->sizing[TABLE_HORZ].n_keeps);
746
747   out->notes = to_utf8_if_nonempty (in->ts->notes, encoding);
748   out->table_look = to_utf8_if_nonempty (in->ts->table_look, encoding);
749
750   /* Print settings. */
751   out->print_all_layers = in->ps->all_layers;
752   out->paginate_layers = in->ps->paginate_layers;
753   out->shrink_to_fit[TABLE_HORZ] = in->ps->fit_width;
754   out->shrink_to_fit[TABLE_VERT] = in->ps->fit_length;
755   out->top_continuation = in->ps->top_continuation;
756   out->bottom_continuation = in->ps->bottom_continuation;
757   out->continuation = xstrdup (in->ps->continuation_string);
758   out->n_orphan_lines = in->ps->n_orphan_lines;
759
760   /* Format settings. */
761   out->epoch = in->formats->y0->epoch;
762   out->decimal = in->formats->y0->decimal;
763   out->grouping = in->formats->y0->grouping;
764   const struct spvlb_custom_currency *cc = in->formats->custom_currency;
765   for (int i = 0; i < 5; i++)
766     if (cc && i < cc->n_ccs)
767       out->ccs[i] = xstrdup (cc->ccs[i]);
768   out->small = in->formats->x3 ? in->formats->x3->small : 0;
769
770   /* Command information. */
771   if (y1)
772     {
773       out->command_local = to_utf8 (y1->command_local, encoding);
774       out->command_c = to_utf8 (y1->command, encoding);
775       out->language = xstrdup (y1->language);
776       /* charset? */
777       out->locale = xstrdup (y1->locale);
778     }
779
780   /* Source information. */
781   const struct spvlb_x3 *x3 = in->formats->x3;
782   if (x3)
783     {
784       if (x3->dataset && x3->dataset[0] && x3->dataset[0] != 4)
785         out->dataset = to_utf8 (x3->dataset, encoding);
786       out->datafile = to_utf8_if_nonempty (x3->datafile, encoding);
787       out->date = x3->date;
788     }
789
790   /* Footnotes.
791
792      Any pivot_value might refer to footnotes, so it's important to process the
793      footnotes early to ensure that those references can be resolved.  There is
794      a possible problem that a footnote might itself reference an
795      as-yet-unprocessed footnote, but that's OK because footnote references
796      don't actually look at the footnote contents but only resolve a pointer to
797      where the footnote will go later.
798
799      Before we really start, create all the footnotes we'll fill in.  This is
800      because sometimes footnotes refer to themselves or to each other and we
801      don't want to reject those references. */
802   const struct spvlb_footnotes *fn = in->footnotes;
803   if (fn->n_footnotes > 0)
804     {
805       pivot_table_create_footnote__ (out, fn->n_footnotes - 1, NULL, NULL);
806       for (size_t i = 0; i < fn->n_footnotes; i++)
807         decode_spvlb_footnote (in->footnotes->footnotes[i], encoding, i, out);
808     }
809
810   /* Title and caption. */
811   out->title = decode_spvlb_value (out, in->titles->user_title, encoding);
812   out->subtype = decode_spvlb_value (out, in->titles->subtype, encoding);
813   if (in->titles->corner_text)
814     out->corner_text = decode_spvlb_value (out, in->titles->corner_text,
815                                            encoding);
816   if (in->titles->caption)
817     out->caption = decode_spvlb_value (out, in->titles->caption, encoding);
818
819   /* Styles. */
820   for (size_t i = 0; i < PIVOT_N_AREAS; i++)
821     decode_spvlb_area (in->areas->areas[i], &out->areas[i], encoding);
822   for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
823     decode_spvlb_border (in->borders->borders[i], out);
824
825   /* Dimensions. */
826   out->n_dimensions = in->dimensions->n_dims;
827   out->dimensions = xcalloc (out->n_dimensions, sizeof *out->dimensions);
828   for (size_t i = 0; i < out->n_dimensions; i++)
829     out->dimensions[i] = decode_spvlb_dimension (out, in->dimensions->dims[i],
830                                                  i, encoding);
831
832   /* Axes. */
833   size_t a = in->axes->n_layers;
834   size_t b = in->axes->n_rows;
835   size_t c = in->axes->n_columns;
836   if (size_overflow_p (xsum3 (a, b, c)) || a + b + c != out->n_dimensions)
837     {
838       fprintf (stderr, "wrong number of dimensions\n");
839       exit (1);
840     }
841   decode_spvlb_axis (in->axes->layers, in->axes->n_layers,
842                      PIVOT_AXIS_LAYER, out);
843   decode_spvlb_axis (in->axes->rows, in->axes->n_rows, PIVOT_AXIS_ROW, out);
844   decode_spvlb_axis (in->axes->columns, in->axes->n_columns,
845                      PIVOT_AXIS_COLUMN, out);
846
847   pivot_table_assign_label_depth (out);
848
849   decode_current_layer (in->ts->current_layer, out);
850
851   /* Data. */
852   decode_spvlb_cells (in->cells->cells, in->cells->n_cells, out, encoding);
853
854   *outp = out;
855   return NULL;
856 }