Add support for reading and writing SPV files.
[pspp] / src / output / spv / spv-legacy-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-legacy-decoder.h"
20
21 #include <errno.h>
22 #include <inttypes.h>
23 #include <math.h>
24 #include <limits.h>
25 #include <stdlib.h>
26
27 #include "data/data-out.h"
28 #include "data/calendar.h"
29 #include "data/format.h"
30 #include "data/value.h"
31 #include "libpspp/assertion.h"
32 #include "libpspp/hash-functions.h"
33 #include "libpspp/hmap.h"
34 #include "libpspp/message.h"
35 #include "output/pivot-table.h"
36 #include "output/spv/detail-xml-parser.h"
37 #include "output/spv/spv-legacy-data.h"
38 #include "output/spv/spv.h"
39 #include "output/spv/structure-xml-parser.h"
40
41 #include "gl/c-strtod.h"
42 #include "gl/xalloc.h"
43 #include "gl/xmemdup0.h"
44
45 #include <libxml/tree.h>
46
47 #include "gettext.h"
48 #define N_(msgid) msgid
49 #define _(msgid) gettext (msgid)
50
51 struct spv_legacy_properties
52   {
53     /* General properties. */
54     bool omit_empty;
55     int width_ranges[TABLE_N_AXES][2];      /* In 1/96" units. */
56     bool row_labels_in_corner;
57
58     /* Footnote display settings. */
59     bool show_numeric_markers;
60     bool footnote_marker_superscripts;
61
62     /* Styles. */
63     struct area_style areas[PIVOT_N_AREAS];
64     struct table_border_style borders[PIVOT_N_BORDERS];
65
66     /* Print settings. */
67     bool print_all_layers;
68     bool paginate_layers;
69     bool shrink_to_width;
70     bool shrink_to_length;
71     bool top_continuation, bottom_continuation;
72     char *continuation;
73     size_t n_orphan_lines;
74   };
75
76 struct spv_series
77   {
78     struct hmap_node hmap_node; /* By name. */
79     char *name;
80     char *label;
81     struct fmt_spec format;
82
83     struct spv_series *label_series;
84     bool is_label_series;
85
86     const struct spvxml_node *xml;
87
88     struct spv_data_value *values;
89     size_t n_values;
90     struct hmap map;            /* Contains "struct spv_mapping". */
91     bool remapped;
92
93     struct pivot_dimension *dimension;
94
95     struct pivot_category **index_to_category;
96     size_t n_index;
97
98     struct spvdx_affix **affixes;
99     size_t n_affixes;
100   };
101
102 static void spv_map_destroy (struct hmap *);
103
104 static struct spv_series *
105 spv_series_first (struct hmap *series_map)
106 {
107   struct spv_series *series;
108   HMAP_FOR_EACH (series, struct spv_series, hmap_node, series_map)
109     return series;
110   return NULL;
111 }
112
113 static struct spv_series *
114 spv_series_find (const struct hmap *series_map, const char *name)
115 {
116   struct spv_series *series;
117   HMAP_FOR_EACH_WITH_HASH (series, struct spv_series, hmap_node,
118                            hash_string (name, 0), series_map)
119     if (!strcmp (name, series->name))
120       return series;
121   return NULL;
122 }
123
124 static struct spv_series *
125 spv_series_from_ref (const struct hmap *series_map,
126                      const struct spvxml_node *ref)
127 {
128   const struct spvxml_node *node
129     = (spvdx_is_source_variable (ref)
130        ? &spvdx_cast_source_variable (ref)->node_
131        : &spvdx_cast_derived_variable (ref)->node_);
132   struct spv_series *series = spv_series_find (series_map, node->id);
133   if (!series)
134     printf ("missing series %s\n", node->id);
135   return series;
136 }
137
138 static void UNUSED
139 spv_series_dump (const struct spv_series *series)
140 {
141   printf ("series \"%s\"", series->name);
142   if (series->label)
143     printf (" (label \"%s\")", series->label);
144   printf (", %zu values:", series->n_values);
145   for (size_t i = 0; i < series->n_values; i++)
146     {
147       putchar (' ');
148       spv_data_value_dump (&series->values[i], stdout);
149     }
150   putchar ('\n');
151 }
152
153 static void
154 spv_series_destroy (struct hmap *series_map)
155 {
156   struct spv_series *series, *next_series;
157   HMAP_FOR_EACH_SAFE (series, next_series, struct spv_series, hmap_node,
158                       series_map)
159     {
160       free (series->name);
161       free (series->label);
162
163       for (size_t i = 0; i < series->n_values; i++)
164         spv_data_value_uninit (&series->values[i]);
165       free (series->values);
166
167       spv_map_destroy (&series->map);
168
169       free (series->index_to_category);
170
171       hmap_delete (series_map, &series->hmap_node);
172       free (series);
173     }
174   hmap_destroy (series_map);
175 }
176
177 struct spv_mapping
178   {
179     struct hmap_node hmap_node;
180     double from;
181     struct spv_data_value to;
182   };
183
184 static struct spv_mapping *
185 spv_map_search (const struct hmap *map, double from)
186 {
187   struct spv_mapping *mapping;
188   HMAP_FOR_EACH_WITH_HASH (mapping, struct spv_mapping, hmap_node,
189                            hash_double (from, 0), map)
190     if (mapping->from == from)
191       return mapping;
192   return NULL;
193 }
194
195 static const struct spv_data_value *
196 spv_map_lookup (const struct hmap *map, const struct spv_data_value *in)
197 {
198   if (in->width >= 0)
199     return in;
200
201   const struct spv_mapping *m = spv_map_search (map, in->d);
202   return m ? &m->to : in;
203 }
204
205 static bool
206 parse_real (const char *s, double *real)
207 {
208   int save_errno = errno;
209   errno = 0;
210   char *end;
211   *real = c_strtod (s, &end);
212   bool ok = !errno && end > s && !*end;
213   errno = save_errno;
214
215   return ok;
216 }
217
218 static char * WARN_UNUSED_RESULT
219 spv_map_insert (struct hmap *map, double from, const char *to,
220                 bool try_strings_as_numbers, const struct fmt_spec *format)
221 {
222   struct spv_mapping *mapping = spv_map_search (map, from);
223
224   if (mapping)
225     return xasprintf ("Duplicate relabeling for from=\"%.*g\"",
226                       DBL_DIG + 1, from);
227   mapping = xmalloc (sizeof *mapping);
228   mapping->from = from;
229
230   if ((try_strings_as_numbers || (format && fmt_is_numeric (format->type)))
231       && parse_real (to, &mapping->to.d))
232     {
233       if (try_strings_as_numbers)
234         mapping->to.width = -1;
235       else
236         {
237           union value v = { .f = mapping->to.d };
238           mapping->to.s = data_out_stretchy (&v, NULL, format, NULL);
239           mapping->to.width = strlen (mapping->to.s);
240         }
241     }
242   else
243     {
244       mapping->to.width = strlen (to);
245       mapping->to.s = xstrdup (to);
246     }
247   hmap_insert (map, &mapping->hmap_node, hash_double (from, 0));
248   return NULL;
249 }
250
251 static void
252 spv_map_destroy (struct hmap *map)
253 {
254   struct spv_mapping *mapping, *next;
255   HMAP_FOR_EACH_SAFE (mapping, next, struct spv_mapping, hmap_node, map)
256     {
257       spv_data_value_uninit (&mapping->to);
258       hmap_delete (map, &mapping->hmap_node);
259       free (mapping);
260     }
261   hmap_destroy (map);
262 }
263
264 static char * WARN_UNUSED_RESULT
265 spv_series_parse_relabels (struct hmap *map,
266                            struct spvdx_relabel **relabels, size_t n_relabels,
267                            bool try_strings_as_numbers,
268                            const struct fmt_spec *format)
269 {
270   for (size_t i = 0; i < n_relabels; i++)
271     {
272       const struct spvdx_relabel *relabel = relabels[i];
273       char *error = spv_map_insert (map, relabel->from, relabel->to,
274                                     try_strings_as_numbers, format);
275       if (error)
276         return error;
277     }
278   return NULL;
279 }
280
281 static char * WARN_UNUSED_RESULT
282 spv_series_parse_value_map_entry (struct hmap *map,
283                                   const struct spvdx_value_map_entry *vme)
284 {
285   for (const char *p = vme->from; ; p++)
286     {
287       int save_errno = errno;
288       errno = 0;
289       char *end;
290       double from = c_strtod (p, &end);
291       bool ok = !errno && end > p && strchr (";", *end);
292       errno = save_errno;
293       if (!ok)
294         return xasprintf ("Syntax error in valueMapEntry from=\"%s\".",
295                           vme->from);
296
297       char *error = spv_map_insert (map, from, vme->to, true,
298                                     &(struct fmt_spec) { FMT_A, 40, 0 });
299       if (error)
300         return error;
301
302       p = end;
303       if (*p == '\0')
304         return NULL;
305       assert (*p == ';');
306     }
307 }
308
309 static struct fmt_spec
310 decode_date_time_format (const struct spvdx_date_time_format *dtf)
311 {
312   if (dtf->dt_base_format == SPVDX_DT_BASE_FORMAT_DATE)
313     {
314       enum fmt_type type
315         = (dtf->show_quarter > 0 ? FMT_QYR
316            : dtf->show_week > 0 ? FMT_WKYR
317            : dtf->mdy_order == SPVDX_MDY_ORDER_DAY_MONTH_YEAR
318            ? (dtf->month_format == SPVDX_MONTH_FORMAT_NUMBER
319               || dtf->month_format == SPVDX_MONTH_FORMAT_PADDED_NUMBER
320               ? FMT_EDATE : FMT_DATE)
321            : dtf->mdy_order == SPVDX_MDY_ORDER_YEAR_MONTH_DAY ? FMT_SDATE
322            : FMT_ADATE);
323
324       int w = fmt_min_output_width (type);
325       if (dtf->year_abbreviation <= 0)
326         w += 2;
327       return (struct fmt_spec) { .type = type, .w = w };
328     }
329   else
330     {
331       enum fmt_type type
332         = (dtf->dt_base_format == SPVDX_DT_BASE_FORMAT_DATE_TIME
333            ? (dtf->mdy_order == SPVDX_MDY_ORDER_YEAR_MONTH_DAY
334               ? FMT_YMDHMS
335               : FMT_DATETIME)
336            : (dtf->show_day > 0 ? FMT_DTIME
337               : dtf->show_hour > 0 ? FMT_TIME
338               : FMT_MTIME));
339       int w = fmt_min_output_width (type);
340       int d = 0;
341       if (dtf->show_second > 0)
342         {
343           w += 3;
344           if (dtf->show_millis > 0)
345             {
346               d = 3;
347               w += d + 1;
348             }
349         }
350       return (struct fmt_spec) { .type = type, .w = w, .d = d };
351     }
352 }
353
354 static struct fmt_spec
355 decode_elapsed_time_format (const struct spvdx_elapsed_time_format *etf)
356 {
357   enum fmt_type type
358     = (etf->dt_base_format != SPVDX_DT_BASE_FORMAT_TIME ? FMT_DTIME
359        : etf->show_hour > 0 ? FMT_TIME
360        : FMT_MTIME);
361   int w = fmt_min_output_width (type);
362   int d = 0;
363   if (etf->show_second > 0)
364     {
365       w += 3;
366       if (etf->show_millis > 0)
367         {
368           d = 3;
369           w += d + 1;
370         }
371     }
372   return (struct fmt_spec) { .type = type, .w = w, .d = d };
373 }
374
375 static struct fmt_spec
376 decode_number_format (const struct spvdx_number_format *nf)
377 {
378   enum fmt_type type = (nf->scientific == SPVDX_SCIENTIFIC_TRUE ? FMT_E
379                         : nf->prefix && !strcmp (nf->prefix, "$") ? FMT_DOLLAR
380                         : nf->suffix && !strcmp (nf->suffix, "%") ? FMT_PCT
381                         : nf->use_grouping ? FMT_COMMA
382                         : FMT_F);
383
384   int d = nf->maximum_fraction_digits;
385   if (d < 0 || d > 15)
386     d = 2;
387
388   struct fmt_spec f = (struct fmt_spec) { type, 40, d };
389   fmt_fix_output (&f);
390   return f;
391 }
392
393 /* Returns an *approximation* of IN as a fmt_spec.
394
395    Not for use with string formats, which don't have any options anyway. */
396 static struct fmt_spec
397 decode_format (const struct spvdx_format *in)
398 {
399   if (in->f_base_format == SPVDX_F_BASE_FORMAT_DATE ||
400       in->f_base_format == SPVDX_F_BASE_FORMAT_TIME ||
401       in->f_base_format == SPVDX_F_BASE_FORMAT_DATE_TIME)
402     {
403       struct spvdx_date_time_format dtf = {
404         .dt_base_format = (in->f_base_format == SPVDX_F_BASE_FORMAT_DATE
405                            ? SPVDX_DT_BASE_FORMAT_DATE
406                            : in->f_base_format == SPVDX_F_BASE_FORMAT_TIME
407                            ? SPVDX_DT_BASE_FORMAT_TIME
408                            : SPVDX_DT_BASE_FORMAT_DATE_TIME),
409         .separator_chars = in->separator_chars,
410         .mdy_order = in->mdy_order,
411         .show_year = in->show_year,
412         .year_abbreviation = in->year_abbreviation,
413         .show_quarter = in->show_quarter,
414         .quarter_prefix = in->quarter_prefix,
415         .quarter_suffix = in->quarter_suffix,
416         .show_month = in->show_month,
417         .month_format = in->month_format,
418         .show_week = in->show_week,
419         .week_padding = in->week_padding,
420         .week_suffix = in->week_suffix,
421         .show_day_of_week = in->show_day_of_week,
422         .day_of_week_abbreviation = in->day_of_week_abbreviation,
423         .day_padding = in->day_padding,
424         .day_of_month_padding = in->day_of_month_padding,
425         .hour_padding = in->hour_padding,
426         .minute_padding = in->minute_padding,
427         .second_padding = in->second_padding,
428         .show_day = in->show_day,
429         .show_hour = in->show_hour,
430         .show_minute = in->show_minute,
431         .show_second = in->show_second,
432         .show_millis = in->show_millis,
433         .day_type = in->day_type,
434         .hour_format = in->hour_format,
435       };
436       return decode_date_time_format (&dtf);
437     }
438   else if (in->f_base_format == SPVDX_F_BASE_FORMAT_ELAPSED_TIME)
439     {
440       struct spvdx_elapsed_time_format etf = {
441         .dt_base_format = (in->f_base_format == SPVDX_F_BASE_FORMAT_DATE
442                            ? SPVDX_DT_BASE_FORMAT_DATE
443                            : in->f_base_format == SPVDX_F_BASE_FORMAT_TIME
444                            ? SPVDX_DT_BASE_FORMAT_TIME
445                            : SPVDX_DT_BASE_FORMAT_DATE_TIME),
446         .day_padding = in->day_padding,
447         .minute_padding = in->minute_padding,
448         .second_padding = in->second_padding,
449         .show_year = in->show_year,
450         .show_day = in->show_day,
451         .show_hour = in->show_hour,
452         .show_minute = in->show_minute,
453         .show_second = in->show_second,
454         .show_millis = in->show_millis,
455       };
456       return decode_elapsed_time_format (&etf);
457     }
458   else
459     {
460       assert (!in->f_base_format);
461       struct spvdx_number_format nf = {
462         .minimum_integer_digits = in->minimum_integer_digits,
463         .maximum_fraction_digits = in->maximum_fraction_digits,
464         .minimum_fraction_digits = in->minimum_fraction_digits,
465         .use_grouping = in->use_grouping,
466         .scientific = in->scientific,
467         .small = in->small,
468         .prefix = in->prefix,
469         .suffix = in->suffix,
470       };
471       return decode_number_format (&nf);
472     }
473 }
474
475 static void
476 spv_series_execute_mapping (struct spv_series *series)
477 {
478   if (!hmap_is_empty (&series->map))
479     {
480       series->remapped = true;
481       for (size_t i = 0; i < series->n_values; i++)
482         {
483           struct spv_data_value *value = &series->values[i];
484           if (value->width >= 0)
485             continue;
486
487           const struct spv_mapping *mapping = spv_map_search (&series->map,
488                                                               value->d);
489           if (mapping)
490             {
491               value->index = value->d;
492               assert (value->index == floor (value->index));
493               value->width = mapping->to.width;
494               if (value->width >= 0)
495                 value->s = xmemdup0 (mapping->to.s, mapping->to.width);
496               else
497                 value->d = mapping->to.d;
498             }
499         }
500     }
501 }
502
503 static char * WARN_UNUSED_RESULT
504 spv_series_remap_formats (struct spv_series *series,
505                           struct spvxml_node **seq, size_t n_seq)
506 {
507   spv_map_destroy (&series->map);
508   hmap_init (&series->map);
509   for (size_t i = 0; i < n_seq; i++)
510     {
511       struct spvxml_node *node = seq[i];
512       if (spvdx_is_format (node))
513         {
514           struct spvdx_format *f = spvdx_cast_format (node);
515           series->format = decode_format (f);
516           char *error = spv_series_parse_relabels (
517             &series->map, f->relabel, f->n_relabel,
518             f->try_strings_as_numbers > 0, &series->format);
519           if (error)
520             return error;
521
522           series->affixes = f->affix;
523           series->n_affixes = f->n_affix;
524         }
525       else if (spvdx_is_string_format (node))
526         {
527           struct spvdx_string_format *sf = spvdx_cast_string_format (node);
528           char *error = spv_series_parse_relabels (&series->map,
529                                                    sf->relabel, sf->n_relabel,
530                                                    false, NULL);
531           if (error)
532             return error;
533
534           series->affixes = sf->affix;
535           series->n_affixes = sf->n_affix;
536         }
537       else
538         NOT_REACHED ();
539     }
540   spv_series_execute_mapping (series);
541   return NULL;
542 }
543
544 static char * WARN_UNUSED_RESULT
545 spv_series_remap_vmes (struct spv_series *series,
546                        struct spvdx_value_map_entry **vmes,
547                        size_t n_vmes)
548 {
549   spv_map_destroy (&series->map);
550   hmap_init (&series->map);
551   for (size_t i = 0; i < n_vmes; i++)
552     {
553       char *error = spv_series_parse_value_map_entry (&series->map, vmes[i]);
554       if (error)
555         return error;
556     }
557   spv_series_execute_mapping (series);
558   return NULL;
559 }
560
561 static void
562 decode_footnotes (struct pivot_table *table, const struct spvdx_footnotes *f)
563 {
564   if (f->n_footnote_mapping > 0)
565     pivot_table_create_footnote__ (table, f->n_footnote_mapping - 1,
566                                    NULL, NULL);
567   for (size_t i = 0; i < f->n_footnote_mapping; i++)
568     {
569       const struct spvdx_footnote_mapping *fm = f->footnote_mapping[i];
570       pivot_table_create_footnote__ (table, fm->defines_reference - 1,
571                                      pivot_value_new_user_text (fm->to, -1),
572                                      NULL);
573     }
574 }
575
576 static struct cell_color
577 optional_color (int color, struct cell_color default_color)
578 {
579   return (color >= 0
580           ? (struct cell_color) CELL_COLOR (color >> 16, color >> 8, color)
581           : default_color);
582 }
583
584 static int
585 optional_length (const char *s, int default_length)
586 {
587   /* There is usually a "pt" suffix.  We ignore it. */
588   int length;
589   return s && sscanf (s, "%d", &length) == 1 ? length : default_length;
590 }
591
592 static int
593 optional_px (double inches, int default_px)
594 {
595   return inches != DBL_MAX ? inches * 96.0 : default_px;
596 }
597
598 static int
599 optional_pt (double inches, int default_pt)
600 {
601   return inches != DBL_MAX ? inches * 72.0 + .5 : default_pt;
602 }
603
604 static void
605 decode_spvdx_style_incremental (const struct spvdx_style *in,
606                                 const struct spvdx_style *bg,
607                                 struct area_style *out)
608 {
609   if (in && in->font_weight)
610     out->font_style.bold = in->font_weight == SPVDX_FONT_WEIGHT_BOLD;
611   if (in && in->font_style)
612     out->font_style.italic = in->font_style == SPVDX_FONT_STYLE_ITALIC;
613   if (in && in->font_underline)
614     out->font_style.underline = in->font_underline == SPVDX_FONT_UNDERLINE_UNDERLINE;
615   if (in && in->color >= 0)
616     {
617       out->font_style.fg[0] = optional_color (
618         in->color, (struct cell_color) CELL_COLOR_BLACK);
619       out->font_style.fg[1] = out->font_style.fg[0];
620     }
621   if (bg && bg->color >= 0)
622     {
623       out->font_style.bg[0] = optional_color (
624         bg->color, (struct cell_color) CELL_COLOR_WHITE);
625       out->font_style.bg[1] = out->font_style.bg[0];
626     }
627   if (in && in->font_family)
628     {
629       free (out->font_style.typeface);
630       out->font_style.typeface = xstrdup (in->font_family);
631     }
632   if (in && in->font_size)
633     {
634       int size = optional_length (in->font_size, 0);
635       if (size)
636         out->font_style.size = size;
637     }
638   if (in && in->text_alignment)
639     out->cell_style.halign
640       = (in->text_alignment == SPVDX_TEXT_ALIGNMENT_LEFT
641          ? TABLE_HALIGN_LEFT
642          : in->text_alignment == SPVDX_TEXT_ALIGNMENT_RIGHT
643          ? TABLE_HALIGN_RIGHT
644          : in->text_alignment == SPVDX_TEXT_ALIGNMENT_CENTER
645          ? TABLE_HALIGN_CENTER
646          : in->text_alignment == SPVDX_TEXT_ALIGNMENT_DECIMAL
647          ? TABLE_HALIGN_DECIMAL
648          : TABLE_HALIGN_MIXED);
649   if (in && in->label_location_vertical)
650     out->cell_style.valign =
651       (in->label_location_vertical == SPVDX_LABEL_LOCATION_VERTICAL_NEGATIVE
652        ? TABLE_VALIGN_BOTTOM
653        : in->label_location_vertical == SPVDX_LABEL_LOCATION_VERTICAL_POSITIVE
654        ? TABLE_VALIGN_TOP
655        : TABLE_VALIGN_CENTER);
656   if (in && in->decimal_offset != DBL_MAX)
657     out->cell_style.decimal_offset = optional_px (in->decimal_offset, 0);
658 #if 0
659   if (in && in->margin_left != DBL_MAX)
660     out->cell_style.margin[TABLE_HORZ][0] = optional_pt (in->margin_left, 8);
661   if (in && in->margin_right != DBL_MAX)
662     out->cell_style.margin[TABLE_HORZ][1] = optional_pt (in->margin_right, 11);
663   if (in && in->margin_top != DBL_MAX)
664     out->cell_style.margin[TABLE_VERT][0] = optional_pt (in->margin_top, 1);
665   if (in && in->margin_bottom != DBL_MAX)
666     out->cell_style.margin[TABLE_VERT][1] = optional_pt (in->margin_bottom, 1);
667 #endif
668 }
669
670 static void
671 decode_spvdx_style (const struct spvdx_style *in,
672                     const struct spvdx_style *bg,
673                     struct area_style *out)
674 {
675   *out = (struct area_style) AREA_STYLE_INITIALIZER;
676   decode_spvdx_style_incremental (in, bg, out);
677 }
678
679 static void
680 add_footnote (struct pivot_value *v, int idx, struct pivot_table *table)
681 {
682   if (idx < 1 || idx > table->n_footnotes)
683     return;
684
685   pivot_value_add_footnote (v, table->footnotes[idx - 1]);
686 }
687
688 static char * WARN_UNUSED_RESULT
689 decode_label_frame (struct pivot_table *table,
690                     const struct spvdx_label_frame *lf)
691 {
692   if (!lf->label)
693     return NULL;
694
695   struct pivot_value **target;
696   struct area_style *area;
697   if (lf->label->purpose == SPVDX_PURPOSE_TITLE)
698     {
699       target = &table->title;
700       area = &table->areas[PIVOT_AREA_TITLE];
701     }
702   else if (lf->label->purpose == SPVDX_PURPOSE_SUB_TITLE)
703     {
704       target = &table->caption;
705       area = &table->areas[PIVOT_AREA_CAPTION];
706     }
707   else if (lf->label->purpose == SPVDX_PURPOSE_FOOTNOTE)
708     {
709       if (lf->label->n_text > 0
710           && lf->label->text[0]->uses_reference != INT_MIN)
711         {
712           target = NULL;
713           area = &table->areas[PIVOT_AREA_FOOTER];
714         }
715       else
716         return NULL;
717     }
718   else if (lf->label->purpose == SPVDX_PURPOSE_LAYER)
719     {
720       target = NULL;
721       area = &table->areas[PIVOT_AREA_LAYERS];
722     }
723   else
724     return NULL;
725
726   area_style_uninit (area);
727   decode_spvdx_style (lf->label->style, lf->label->text_frame_style, area);
728
729   if (target)
730     {
731       struct pivot_value *value = xzalloc (sizeof *value);
732       value->type = PIVOT_VALUE_TEXT;
733       for (size_t i = 0; i < lf->label->n_text; i++)
734         {
735           const struct spvdx_text *in = lf->label->text[i];
736           if (in->defines_reference != INT_MIN)
737             add_footnote (value, in->defines_reference, table);
738           else if (!value->text.local)
739             value->text.local = xstrdup (in->text);
740           else
741             {
742               char *new = xasprintf ("%s%s", value->text.local, in->text);
743               free (value->text.local);
744               value->text.local = new;
745             }
746         }
747       pivot_value_destroy (*target);
748       *target = value;
749     }
750   else
751     for (size_t i = 0; i < lf->label->n_text; i++)
752       {
753         const struct spvdx_text *in = lf->label->text[i];
754         if (in->uses_reference == INT_MIN)
755           continue;
756         if (i % 2)
757           {
758             size_t length = strlen (in->text);
759             if (length && in->text[length - 1] == '\n')
760               length--;
761
762             pivot_table_create_footnote__ (
763               table, in->uses_reference - 1, NULL,
764               pivot_value_new_user_text (in->text, length));
765           }
766         else
767           {
768             size_t length = strlen (in->text);
769             if (length && in->text[length - 1] == '.')
770               length--;
771
772             pivot_table_create_footnote__ (
773               table, in->uses_reference - 1,
774               pivot_value_new_user_text (in->text, length), NULL);
775           }
776       }
777   return NULL;
778 }
779
780 /* Special return value for decode_spvdx_variable(). */
781 static char BAD_REFERENCE;
782
783 static char * WARN_UNUSED_RESULT
784 decode_spvdx_source_variable (const struct spvxml_node *node,
785                               struct spv_data *data,
786                               struct hmap *series_map)
787 {
788   const struct spvdx_source_variable *sv = spvdx_cast_source_variable (node);
789
790   struct spv_series *label_series = NULL;
791   if (sv->label_variable)
792     {
793       label_series = spv_series_find (series_map,
794                                       sv->label_variable->node_.id);
795       if (!label_series)
796         return &BAD_REFERENCE;
797
798       label_series->is_label_series = true;
799     }
800
801   const struct spv_data_variable *var = spv_data_find_variable (
802     data, sv->source, sv->source_name);
803   if (!var)
804     return xasprintf ("sourceVariable %s references nonexistent "
805                       "source %s variable %s.",
806                       sv->node_.id, sv->source, sv->source_name);
807
808   struct spv_series *s = xzalloc (sizeof *s);
809   s->name = xstrdup (node->id);
810   s->xml = node;
811   s->label = sv->label ? xstrdup (sv->label) : NULL;
812   s->label_series = label_series;
813   s->values = spv_data_values_clone (var->values, var->n_values);
814   s->n_values = var->n_values;
815   s->format = F_8_0;
816   hmap_init (&s->map);
817   hmap_insert (series_map, &s->hmap_node, hash_string (s->name, 0));
818
819   char *error = spv_series_remap_formats (s, sv->seq, sv->n_seq);
820   if (error)
821     return error;
822
823   if (label_series && !s->remapped)
824     {
825       for (size_t i = 0; i < s->n_values; i++)
826         if (s->values[i].width < 0)
827           {
828             char *dest;
829             if (label_series->values[i].width < 0)
830               {
831                 union value v = { .f = label_series->values[i].d };
832                 dest = data_out_stretchy (&v, "UTF-8", &s->format, NULL);
833               }
834             else
835               dest = label_series->values[i].s;
836             char *error = spv_map_insert (&s->map, s->values[i].d,
837                                           dest, false, NULL);
838             free (error);   /* Duplicates are OK. */
839             if (label_series->values[i].width < 0)
840               free (dest);
841           }
842     }
843
844   return NULL;
845 }
846
847 static char * WARN_UNUSED_RESULT
848 decode_spvdx_derived_variable (const struct spvxml_node *node,
849                                struct hmap *series_map)
850 {
851   const struct spvdx_derived_variable *dv = spvdx_cast_derived_variable (node);
852
853   struct spv_data_value *values;
854   size_t n_values;
855
856   struct substring value = ss_cstr (dv->value);
857   if (ss_equals (value, ss_cstr ("constant(0)")))
858     {
859       struct spv_series *existing_series = spv_series_first (series_map);
860       if (!existing_series)
861         return &BAD_REFERENCE;
862
863       n_values = existing_series->n_values;
864       values = xcalloc (n_values, sizeof *values);
865       for (size_t i = 0; i < n_values; i++)
866         values[i].width = -1;
867     }
868   else if (ss_starts_with (value, ss_cstr ("constant(")))
869     {
870       values = NULL;
871       n_values = 0;
872     }
873   else if (ss_starts_with (value, ss_cstr ("map("))
874            && ss_ends_with (value, ss_cstr (")")))
875     {
876       char *dependency_name = ss_xstrdup (ss_substr (value, 4,
877                                                      value.length - 5));
878       struct spv_series *dependency
879         = spv_series_find (series_map, dependency_name);
880       free (dependency_name);
881       if (!dependency)
882         return &BAD_REFERENCE;
883
884       values = spv_data_values_clone (dependency->values,
885                                       dependency->n_values);
886       n_values = dependency->n_values;
887     }
888   else
889     return xasprintf ("Derived variable %s has unknown value \"%s\"",
890                       node->id, dv->value);
891
892   struct spv_series *s = xzalloc (sizeof *s);
893   s->format = F_8_0;
894   s->name = xstrdup (node->id);
895   s->values = values;
896   s->n_values = n_values;
897   hmap_init (&s->map);
898   hmap_insert (series_map, &s->hmap_node, hash_string (s->name, 0));
899
900   char *error = spv_series_remap_vmes (s, dv->value_map_entry,
901                                        dv->n_value_map_entry);
902   if (error)
903     return error;
904
905   error = spv_series_remap_formats (s, dv->seq, dv->n_seq);
906   if (error)
907     return error;
908
909   if (n_values > 0)
910     {
911       for (size_t i = 0; i < n_values; i++)
912         if (values[i].width != 0)
913           goto nonempty;
914       for (size_t i = 0; i < n_values; i++)
915         spv_data_value_uninit (&s->values[i]);
916       free (s->values);
917
918       s->values = NULL;
919       s->n_values = 0;
920
921     nonempty:;
922     }
923   return NULL;
924 }
925
926 struct format_mapping
927   {
928     struct hmap_node hmap_node;
929     uint32_t from;
930     struct fmt_spec to;
931   };
932
933 static const struct format_mapping *
934 format_map_find (const struct hmap *format_map, uint32_t u32_format)
935 {
936   if (format_map)
937     {
938       const struct format_mapping *fm;
939       HMAP_FOR_EACH_IN_BUCKET (fm, struct format_mapping, hmap_node,
940                                hash_int (u32_format, 0), format_map)
941         if (fm->from == u32_format)
942           return fm;
943     }
944
945   return NULL;
946 }
947
948 static struct fmt_spec
949 spv_format_from_data_value (const struct spv_data_value *data,
950                             const struct hmap *format_map)
951 {
952   if (!data)
953     return fmt_for_output (FMT_F, 40, 2);
954
955   uint32_t u32_format = data->width < 0 ? data->d : atoi (data->s);
956   const struct format_mapping *fm = format_map_find (format_map, u32_format);
957   return fm ? fm->to : spv_decode_fmt_spec (u32_format);
958 }
959
960 static struct pivot_value *
961 pivot_value_from_data_value (const struct spv_data_value *data,
962                              const struct spv_data_value *format,
963                              const struct hmap *format_map)
964 {
965   struct pivot_value *v = xzalloc (sizeof *v);
966   struct fmt_spec f = spv_format_from_data_value (format, format_map);
967   if (data->width >= 0)
968     {
969       if (format && fmt_get_category (f.type) == FMT_CAT_DATE)
970         {
971           int year, month, day, hour, minute, second, msec, len = -1;
972           if (sscanf (data->s, "%4d-%2d-%2dT%2d:%2d:%2d.%3d%n",
973                       &year, &month, &day, &hour, &minute, &second,
974                       &msec, &len) == 7
975               && len == 23
976               && data->s[len] == '\0')
977             {
978               double date = calendar_gregorian_to_offset (year, month, day,
979                                                           NULL);
980               if (date != SYSMIS)
981                 {
982                   v->type = PIVOT_VALUE_NUMERIC;
983                   v->numeric.x = (date * 60. * 60. * 24.
984                                   + hour * 60. * 60.
985                                   + minute * 60.
986                                   + second
987                                   + msec / 1000.0);
988                   v->numeric.format = f;
989                   return v;
990                 }
991             }
992         }
993       else if (format && fmt_get_category (f.type) == FMT_CAT_TIME)
994         {
995           int hour, minute, second, msec, len = -1;
996           if (sscanf (data->s, "%d:%2d:%2d.%3d%n",
997                       &hour, &minute, &second, &msec, &len) == 4
998               && len > 0
999               && data->s[len] == '\0')
1000             {
1001               v->type = PIVOT_VALUE_NUMERIC;
1002               v->numeric.x = (hour * 60. * 60.
1003                               + minute * 60.
1004                               + second
1005                               + msec / 1000.0);
1006               v->numeric.format = f;
1007               return v;
1008             }
1009         }
1010       v->type = PIVOT_VALUE_STRING;
1011       v->string.s = xstrdup (data->s);
1012     }
1013   else
1014     {
1015       v->type = PIVOT_VALUE_NUMERIC;
1016       v->numeric.x = data->d;
1017       v->numeric.format = f;
1018     }
1019   return v;
1020 }
1021
1022 static void
1023 add_parents (struct pivot_category *cat, struct pivot_category *parent,
1024              size_t group_index)
1025 {
1026   cat->parent = parent;
1027   cat->group_index = group_index;
1028   if (pivot_category_is_group (cat))
1029     for (size_t i = 0; i < cat->n_subs; i++)
1030       add_parents (cat->subs[i], cat, i);
1031 }
1032
1033 static const struct spvdx_facet_level *
1034 find_facet_level (const struct spvdx_visualization *v, int facet_level)
1035 {
1036   const struct spvdx_facet_layout *layout = v->graph->facet_layout;
1037   for (size_t i = 0; i < layout->n_facet_level; i++)
1038     {
1039       const struct spvdx_facet_level *fl = layout->facet_level[i];
1040       if (facet_level == fl->level)
1041         return fl;
1042     }
1043   return NULL;
1044 }
1045
1046 static bool
1047 should_show_label (const struct spvdx_facet_level *fl)
1048 {
1049   return fl && fl->axis->label && fl->axis->label->style->visible != 0;
1050 }
1051
1052 static size_t
1053 max_category (const struct spv_series *s)
1054 {
1055   double max_cat = -DBL_MAX;
1056   for (size_t i = 0; i < s->n_values; i++)
1057     {
1058       const struct spv_data_value *dv = &s->values[i];
1059       double d = dv->width < 0 ? dv->d : dv->index;
1060       if (d > max_cat)
1061         max_cat = d;
1062     }
1063   assert (max_cat >= 0 && max_cat < SIZE_MAX - 1);
1064
1065   return max_cat;
1066 }
1067
1068 static void
1069 add_affixes (struct pivot_table *table, struct pivot_value *value,
1070              struct spvdx_affix **affixes, size_t n_affixes)
1071 {
1072   for (size_t i = 0; i < n_affixes; i++)
1073     add_footnote (value, affixes[i]->defines_reference, table);
1074 }
1075
1076 static struct pivot_dimension *
1077 add_dimension (struct spv_series **series, size_t n,
1078                enum pivot_axis_type axis_type,
1079                const struct spvdx_visualization *v, struct pivot_table *table,
1080                struct spv_series **dim_seriesp, size_t *n_dim_seriesp,
1081                int base_facet_level)
1082 {
1083   const struct spvdx_facet_level *fl
1084     = find_facet_level (v, base_facet_level + n);
1085   if (fl)
1086     {
1087       struct area_style *area = (axis_type == PIVOT_AXIS_COLUMN
1088                                  ? &table->areas[PIVOT_AREA_COLUMN_LABELS]
1089                                  : axis_type == PIVOT_AXIS_ROW
1090                                  ? &table->areas[PIVOT_AREA_ROW_LABELS]
1091                                  : NULL);
1092       if (area && fl->axis->label)
1093         {
1094           area_style_uninit (area);
1095           decode_spvdx_style (fl->axis->label->style,
1096                               fl->axis->label->text_frame_style, area);
1097         }
1098     }
1099
1100   if (axis_type == PIVOT_AXIS_ROW)
1101     {
1102       const struct spvdx_facet_level *fl2
1103         = find_facet_level (v, base_facet_level + (n - 1));
1104       if (fl2)
1105         decode_spvdx_style_incremental (
1106           fl2->axis->major_ticks->style,
1107           fl2->axis->major_ticks->tick_frame_style,
1108           &table->areas[PIVOT_AREA_ROW_LABELS]);
1109     }
1110
1111   const struct spvdx_facet_level *fl3 = find_facet_level (v, base_facet_level);
1112   if (fl3 && fl3->axis->major_ticks->label_angle == -90)
1113     {
1114       if (axis_type == PIVOT_AXIS_COLUMN)
1115         table->rotate_inner_column_labels = true;
1116       else
1117         table->rotate_outer_row_labels = true;
1118     }
1119
1120   /* Find the first row for each category. */
1121   size_t max_cat = max_category (series[0]);
1122   size_t *cat_rows = xnmalloc (max_cat + 1, sizeof *cat_rows);
1123   for (size_t k = 0; k <= max_cat; k++)
1124     cat_rows[k] = SIZE_MAX;
1125   for (size_t k = 0; k < series[0]->n_values; k++)
1126     {
1127       const struct spv_data_value *dv = &series[0]->values[k];
1128       double d = dv->width < 0 ? dv->d : dv->index;
1129       if (d >= 0 && d < SIZE_MAX - 1)
1130         {
1131           size_t row = d;
1132           if (cat_rows[row] == SIZE_MAX)
1133             cat_rows[row] = k;
1134         }
1135     }
1136
1137   /* Drop missing categories and count what's left. */
1138   size_t n_cats = 0;
1139   for (size_t k = 0; k <= max_cat; k++)
1140     if (cat_rows[k] != SIZE_MAX)
1141       cat_rows[n_cats++] = cat_rows[k];
1142   assert (n_cats > 0);
1143
1144   /* Make the categories. */
1145   struct pivot_dimension *d = xzalloc (sizeof *d);
1146   table->dimensions[table->n_dimensions++] = d;
1147
1148   series[0]->n_index = max_cat + 1;
1149   series[0]->index_to_category = xcalloc (
1150     max_cat + 1, sizeof *series[0]->index_to_category);
1151   struct pivot_category **cats = xnmalloc (n_cats, sizeof **cats);
1152   for (size_t k = 0; k < n_cats; k++)
1153     {
1154       struct spv_data_value *dv = &series[0]->values[cat_rows[k]];
1155       int dv_num = dv ? dv->d : dv->index;
1156       struct pivot_category *cat = xzalloc (sizeof *cat);
1157       cat->name = pivot_value_from_data_value (
1158         spv_map_lookup (&series[0]->map, dv), NULL, NULL);
1159       cat->parent = NULL;
1160       cat->dimension = d;
1161       cat->data_index = k;
1162       cat->presentation_index = cat_rows[k];
1163       cats[k] = cat;
1164       series[0]->index_to_category[dv_num] = cat;
1165
1166       add_affixes (table, cat->name, series[0]->affixes, series[0]->n_affixes);
1167     }
1168   free (cat_rows);
1169
1170   struct pivot_axis *axis = &table->axes[axis_type];
1171   d->axis_type = axis_type;
1172   d->level = axis->n_dimensions;
1173   d->top_index = table->n_dimensions - 1;
1174   d->root = xzalloc (sizeof *d->root);
1175   *d->root = (struct pivot_category) {
1176     .name = pivot_value_new_user_text (
1177       series[0]->label ? series[0]->label : "", -1),
1178     .dimension = d,
1179     .show_label = should_show_label (fl),
1180     .data_index = SIZE_MAX,
1181     .presentation_index = SIZE_MAX,
1182   };
1183   d->data_leaves = xmemdup (cats, n_cats * sizeof *cats);
1184   d->presentation_leaves = xmemdup (cats, n_cats * sizeof *cats);
1185   d->n_leaves = d->allocated_leaves = n_cats;
1186
1187   /* Now group them, in one pass per grouping variable, innermost first. */
1188   for (size_t j = 1; j < n; j++)
1189     {
1190       struct pivot_category **new_cats = xnmalloc (n_cats, sizeof **cats);
1191       size_t n_new_cats = 0;
1192
1193       /* Allocate a category index. */
1194       size_t max_cat = max_category (series[j]);
1195       series[j]->n_index = max_cat + 1;
1196       series[j]->index_to_category = xcalloc (
1197         max_cat + 1, sizeof *series[j]->index_to_category);
1198       for (size_t cat1 = 0; cat1 < n_cats; )
1199         {
1200           /* Find a sequence of categories cat1...cat2 (exclusive), that all
1201              have the same value in series 'j'.  (This might be only a single
1202              category; we will drop unnamed 1-category groups later.) */
1203           size_t row1 = cats[cat1]->presentation_index;
1204           const struct spv_data_value *dv1 = &series[j]->values[row1];
1205           size_t cat2;
1206           for (cat2 = cat1 + 1; cat2 < n_cats; cat2++)
1207             {
1208               size_t row2 = cats[cat2]->presentation_index;
1209               const struct spv_data_value *dv2 = &series[j]->values[row2];
1210               if (!spv_data_value_equal (dv1, dv2))
1211                 break;
1212             }
1213           size_t n_subs = cat2 - cat1;
1214
1215           struct pivot_category *new_cat;
1216           const struct spv_data_value *name
1217             = spv_map_lookup (&series[j]->map, dv1);
1218           if (n_subs == 1 && name->width == 0)
1219             {
1220               /* The existing category stands on its own. */
1221               new_cat = cats[cat1++];
1222             }
1223           else
1224             {
1225               /* Create a new group with cat...cat2 as subcategories. */
1226               new_cat = xzalloc (sizeof *new_cat);
1227               *new_cat = (struct pivot_category) {
1228                 .name = pivot_value_from_data_value (name, NULL, NULL),
1229                 .dimension = d,
1230                 .subs = xnmalloc (n_subs, sizeof *new_cat->subs),
1231                 .n_subs = n_subs,
1232                 .show_label = true,
1233                 .data_index = SIZE_MAX,
1234                 .presentation_index = row1,
1235               };
1236               for (size_t k = 0; k < n_subs; k++)
1237                 new_cat->subs[k] = cats[cat1++];
1238
1239               int dv1_num = dv1->width < 0 ? dv1->d : dv1->index;
1240               series[j]->index_to_category[dv1_num] = new_cat;
1241             }
1242
1243           add_affixes (table, new_cat->name,
1244                        series[j]->affixes, series[j]->n_affixes);
1245
1246           /* Append the new group to the list of new groups. */
1247           new_cats[n_new_cats++] = new_cat;
1248         }
1249
1250       free (cats);
1251       cats = new_cats;
1252       n_cats = n_new_cats;
1253     }
1254
1255   /* Now drop unnamed 1-category groups and add parent pointers. */
1256   for (size_t j = 0; j < n_cats; j++)
1257     add_parents (cats[j], d->root, j);
1258
1259   d->root->subs = cats;
1260   d->root->n_subs = n_cats;
1261
1262   dim_seriesp[(*n_dim_seriesp)++] = series[0];
1263   series[0]->dimension = d;
1264
1265   axis->dimensions = xnrealloc (axis->dimensions, axis->n_dimensions + 1,
1266                                sizeof *axis->dimensions);
1267   axis->dimensions[axis->n_dimensions++] = d;
1268   axis->extent *= d->n_leaves;
1269
1270   return d;
1271 }
1272
1273 static void
1274 add_dimensions (struct hmap *series_map, const struct spvdx_nest *nest,
1275                 enum pivot_axis_type axis_type,
1276                 const struct spvdx_visualization *v, struct pivot_table *table,
1277                 struct spv_series **dim_seriesp, size_t *n_dim_seriesp,
1278                 int level_ofs)
1279 {
1280   struct pivot_axis *axis = &table->axes[axis_type];
1281   if (!axis->extent)
1282     axis->extent = 1;
1283
1284   if (!nest)
1285     return;
1286
1287   struct spv_series **series = xnmalloc (nest->n_vars, sizeof *series);
1288   for (size_t i = 0; i < nest->n_vars; )
1289     {
1290       size_t n;
1291       for (n = 0; i + n < nest->n_vars; n++)
1292         {
1293           series[n] = spv_series_from_ref (series_map, nest->vars[i + n]->ref);
1294           if (!series[n] || !series[n]->n_values)
1295             break;
1296         }
1297
1298       if (n > 0)
1299         add_dimension (series, n, axis_type, v, table,
1300                        dim_seriesp, n_dim_seriesp, level_ofs + i);
1301       i += n + 1;
1302     }
1303   free (series);
1304 }
1305
1306 static void
1307 add_layers (struct hmap *series_map,
1308             struct spvdx_layer **layers, size_t n_layers,
1309             const struct spvdx_visualization *v, struct pivot_table *table,
1310             struct spv_series **dim_seriesp, size_t *n_dim_seriesp,
1311             int level_ofs)
1312 {
1313   struct pivot_axis *axis = &table->axes[PIVOT_AXIS_LAYER];
1314   if (!axis->extent)
1315     axis->extent = 1;
1316
1317   if (!n_layers)
1318     return;
1319
1320   struct spv_series **series = xnmalloc (n_layers, sizeof *series);
1321   for (size_t i = 0; i < n_layers; )
1322     {
1323       size_t n;
1324       for (n = 0; i + n < n_layers; n++)
1325         {
1326           series[n] = spv_series_from_ref (series_map,
1327                                            layers[i + n]->variable);
1328           if (!series[n] || !series[n]->n_values)
1329             break;
1330         }
1331
1332       if (n > 0)
1333         {
1334           struct pivot_dimension *d = add_dimension (
1335             series, n, PIVOT_AXIS_LAYER, v, table,
1336             dim_seriesp, n_dim_seriesp, level_ofs + i);
1337
1338           int index = atoi (layers[i]->value);
1339           assert (index < d->n_leaves);
1340           table->current_layer = xrealloc (
1341             table->current_layer,
1342             axis->n_dimensions * sizeof *table->current_layer);
1343           table->current_layer[axis->n_dimensions - 1] = index;
1344         }
1345       i += n + 1;
1346     }
1347   free (series);
1348 }
1349
1350 static int
1351 optional_int (int x, int default_value)
1352 {
1353   return x != INT_MIN ? x : default_value;
1354 }
1355
1356 static enum pivot_area
1357 pivot_area_from_name (const char *name)
1358 {
1359   static const char *area_names[PIVOT_N_AREAS] = {
1360     [PIVOT_AREA_TITLE] = "title",
1361     [PIVOT_AREA_CAPTION] = "caption",
1362     [PIVOT_AREA_FOOTER] = "footnotes",
1363     [PIVOT_AREA_CORNER] = "cornerLabels",
1364     [PIVOT_AREA_COLUMN_LABELS] = "columnLabels",
1365     [PIVOT_AREA_ROW_LABELS] = "rowLabels",
1366     [PIVOT_AREA_DATA] = "data",
1367     [PIVOT_AREA_LAYERS] = "layers",
1368   };
1369
1370   enum pivot_area area;
1371   for (area = 0; area < PIVOT_N_AREAS; area++)
1372     if (!strcmp (name, area_names[area]))
1373       break;
1374   return area;
1375 }
1376
1377 static enum pivot_border
1378 pivot_border_from_name (const char *name)
1379 {
1380   static const char *border_names[PIVOT_N_BORDERS] = {
1381     [PIVOT_BORDER_TITLE] = "titleLayerSeparator",
1382     [PIVOT_BORDER_OUTER_LEFT] = "leftOuterFrame",
1383     [PIVOT_BORDER_OUTER_TOP] = "topOuterFrame",
1384     [PIVOT_BORDER_OUTER_RIGHT] = "rightOuterFrame",
1385     [PIVOT_BORDER_OUTER_BOTTOM] = "bottomOuterFrame",
1386     [PIVOT_BORDER_INNER_LEFT] = "leftInnerFrame",
1387     [PIVOT_BORDER_INNER_TOP] = "topInnerFrame",
1388     [PIVOT_BORDER_INNER_RIGHT] = "rightInnerFrame",
1389     [PIVOT_BORDER_INNER_BOTTOM] = "bottomInnerFrame",
1390     [PIVOT_BORDER_DATA_LEFT] = "dataAreaLeft",
1391     [PIVOT_BORDER_DATA_TOP] = "dataAreaTop",
1392     [PIVOT_BORDER_DIM_ROW_HORZ] = "horizontalDimensionBorderRows",
1393     [PIVOT_BORDER_DIM_ROW_VERT] = "verticalDimensionBorderRows",
1394     [PIVOT_BORDER_DIM_COL_HORZ] = "horizontalDimensionBorderColumns",
1395     [PIVOT_BORDER_DIM_COL_VERT] = "verticalDimensionBorderColumns",
1396     [PIVOT_BORDER_CAT_ROW_HORZ] = "horizontalCategoryBorderRows",
1397     [PIVOT_BORDER_CAT_ROW_VERT] = "verticalCategoryBorderRows",
1398     [PIVOT_BORDER_CAT_COL_HORZ] = "horizontalCategoryBorderColumns",
1399     [PIVOT_BORDER_CAT_COL_VERT] = "verticalCategoryBorderColumns",
1400   };
1401
1402   enum pivot_border border;
1403   for (border = 0; border < PIVOT_N_BORDERS; border++)
1404     if (!strcmp (name, border_names[border]))
1405       break;
1406   return border;
1407 }
1408
1409 static struct pivot_category *
1410 find_category (struct spv_series *series, int index)
1411 {
1412   return (index >= 0 && index < series->n_index
1413           ? series->index_to_category[index]
1414           : NULL);
1415 }
1416
1417 static bool
1418 int_in_array (int value, const int *array, size_t n)
1419 {
1420   for (size_t i = 0; i < n; i++)
1421     if (array[i] == value)
1422       return true;
1423
1424   return false;
1425 }
1426
1427 static void
1428 apply_styles_to_value (struct pivot_table *table,
1429                        struct pivot_value *value,
1430                        const struct spvdx_set_format *sf,
1431                        const struct area_style *base_area_style,
1432                        const struct spvdx_style *fg,
1433                        const struct spvdx_style *bg)
1434 {
1435   if (sf)
1436     {
1437       if (sf->reset > 0)
1438         {
1439           free (value->footnotes);
1440           value->footnotes = NULL;
1441           value->n_footnotes = 0;
1442         }
1443
1444       struct fmt_spec format = { .w = 0 };
1445       if (sf->format)
1446         {
1447           format = decode_format (sf->format);
1448           add_affixes (table, value, sf->format->affix, sf->format->n_affix);
1449         }
1450       else if (sf->number_format)
1451         {
1452           format = decode_number_format (sf->number_format);
1453           add_affixes (table, value, sf->number_format->affix,
1454                        sf->number_format->n_affix);
1455         }
1456       else if (sf->n_string_format)
1457         {
1458           for (size_t i = 0; i < sf->n_string_format; i++)
1459             add_affixes (table, value, sf->string_format[i]->affix,
1460                          sf->string_format[i]->n_affix);
1461         }
1462       else if (sf->date_time_format)
1463         {
1464           format = decode_date_time_format (sf->date_time_format);
1465           add_affixes (table, value, sf->date_time_format->affix,
1466                        sf->date_time_format->n_affix);
1467         }
1468       else if (sf->elapsed_time_format)
1469         {
1470           format = decode_elapsed_time_format (sf->elapsed_time_format);
1471           add_affixes (table, value, sf->elapsed_time_format->affix,
1472                        sf->elapsed_time_format->n_affix);
1473         }
1474
1475       if (format.w)
1476         {
1477           if (value->type == PIVOT_VALUE_NUMERIC)
1478             value->numeric.format = format;
1479
1480           /* Possibly we should try to apply date and time formats too,
1481              but none seem to occur in practice so far. */
1482         }
1483     }
1484   if (fg || bg)
1485     {
1486       struct area_style area;
1487       pivot_value_get_style (
1488         value,
1489         value->font_style ? value->font_style : &base_area_style->font_style,
1490         value->cell_style ? value->cell_style : &base_area_style->cell_style,
1491         &area);
1492       decode_spvdx_style_incremental (fg, bg, &area);
1493       pivot_value_set_style (value, &area);
1494       area_style_uninit (&area);
1495     }
1496 }
1497
1498 static void
1499 decode_set_cell_properties__ (struct pivot_table *table,
1500                               struct hmap *series_map,
1501                               const struct spvdx_intersect *intersect,
1502                               const struct spvdx_style *interval,
1503                               const struct spvdx_style *graph,
1504                               const struct spvdx_style *labeling,
1505                               const struct spvdx_style *frame,
1506                               const struct spvdx_style *major_ticks,
1507                               const struct spvdx_set_format *set_format)
1508 {
1509   if (graph && labeling && intersect->alternating
1510       && !interval && !major_ticks && !frame && !set_format)
1511     {
1512       /* Sets alt_fg_color and alt_bg_color. */
1513       struct area_style area;
1514       decode_spvdx_style (labeling, graph, &area);
1515       table->areas[PIVOT_AREA_DATA].font_style.fg[1]
1516         = area.font_style.fg[0];
1517       table->areas[PIVOT_AREA_DATA].font_style.bg[1]
1518         = area.font_style.bg[0];
1519       area_style_uninit (&area);
1520     }
1521   else if (graph
1522            && !labeling && !interval && !major_ticks && !frame && !set_format)
1523     {
1524       /* 'graph->width' likely just sets the width of the table as a
1525          whole.  */
1526     }
1527   else if (!graph && !labeling && !interval && !frame && !set_format
1528            && !major_ticks)
1529     {
1530       /* No-op.  (Presumably there's a setMetaData we don't care about.) */
1531     }
1532   else if (((set_format && spvdx_is_major_ticks (set_format->target))
1533             || major_ticks || frame)
1534            && intersect->n_where == 1)
1535     {
1536       /* Formatting for individual row or column labels. */
1537       const struct spvdx_where *w = intersect->where[0];
1538       struct spv_series *s = spv_series_find (series_map, w->variable->id);
1539       assert (s);
1540
1541       const char *p = w->include;
1542
1543       while (*p)
1544         {
1545           char *tail;
1546           int include = strtol (p, &tail, 10);
1547
1548           struct pivot_category *c = find_category (s, include);
1549           if (c)
1550             {
1551               const struct area_style *base_area_style
1552                 = (c->dimension->axis_type == PIVOT_AXIS_ROW
1553                    ? &table->areas[PIVOT_AREA_ROW_LABELS]
1554                    : &table->areas[PIVOT_AREA_COLUMN_LABELS]);
1555               apply_styles_to_value (table, c->name, set_format,
1556                                      base_area_style, major_ticks, frame);
1557             }
1558
1559           if (tail == p)
1560             break;
1561           p = tail;
1562           if (*p == ';')
1563             p++;
1564         }
1565     }
1566   else if ((set_format && spvdx_is_labeling (set_format->target))
1567            || labeling || interval)
1568     {
1569       /* Formatting for individual cells or groups of them with some dimensions
1570          in common. */
1571       int **indexes = xcalloc (table->n_dimensions, sizeof *indexes);
1572       size_t *n = xcalloc (table->n_dimensions, sizeof *n);
1573       size_t *allocated = xcalloc (table->n_dimensions, sizeof *allocated);
1574
1575       for (size_t i = 0; i < intersect->n_where; i++)
1576         {
1577           const struct spvdx_where *w = intersect->where[i];
1578           struct spv_series *s = spv_series_find (series_map, w->variable->id);
1579           assert (s);
1580           if (!s->dimension)
1581             {
1582               /* Group indexes may be included even though they are redundant.
1583                  Ignore them. */
1584               continue;
1585             }
1586
1587           size_t j = s->dimension->top_index;
1588
1589           const char *p = w->include;
1590           while (*p)
1591             {
1592               char *tail;
1593               int include = strtol (p, &tail, 10);
1594
1595               struct pivot_category *c = find_category (s, include);
1596               if (c)
1597                 {
1598                   if (n[j] >= allocated[j])
1599                     indexes[j] = x2nrealloc (indexes[j], &allocated[j],
1600                                              sizeof *indexes[j]);
1601                   indexes[j][n[j]++] = c->data_index;
1602                 }
1603
1604               if (tail == p)
1605                 break;
1606               p = tail;
1607               if (*p == ';')
1608                 p++;
1609             }
1610         }
1611
1612 #if 0
1613       printf ("match:");
1614       for (size_t i = 0; i < table->n_dimensions; i++)
1615         {
1616           if (n[i])
1617             {
1618               printf (" %d=(", i);
1619               for (size_t j = 0; j < n[i]; j++)
1620                 {
1621                   if (j)
1622                     putchar (',');
1623                   printf ("%d", indexes[i][j]);
1624                 }
1625               putchar (')');
1626             }
1627         }
1628       printf ("\n");
1629 #endif
1630
1631       /* XXX This is inefficient in the common case where all of the dimensions
1632          are matched.  We should use a heuristic where if all of the dimensions
1633          are matched and the product of n[*] is less than
1634          hmap_count(&table->cells) then iterate through all the possibilities
1635          rather than all the cells.  Or even only do it if there is just one
1636          possibility. */
1637
1638       struct pivot_cell *cell;
1639       HMAP_FOR_EACH (cell, struct pivot_cell, hmap_node, &table->cells)
1640         {
1641           for (size_t i = 0; i < table->n_dimensions; i++)
1642             {
1643               if (n[i] && !int_in_array (cell->idx[i], indexes[i], n[i]))
1644                 goto skip;
1645             }
1646           apply_styles_to_value (table, cell->value, set_format,
1647                                  &table->areas[PIVOT_AREA_DATA],
1648                                  labeling, interval);
1649
1650         skip: ;
1651         }
1652
1653       for (size_t i = 0; i < table->n_dimensions; i++)
1654         free (indexes[i]);
1655       free (indexes);
1656       free (n);
1657       free (allocated);
1658     }
1659   else
1660     NOT_REACHED ();
1661 }
1662
1663 static void
1664 decode_set_cell_properties (struct pivot_table *table, struct hmap *series_map,
1665                             struct spvdx_set_cell_properties **scps,
1666                             size_t n_scps)
1667 {
1668   for (size_t i = 0; i < n_scps; i++)
1669     {
1670       const struct spvdx_set_cell_properties *scp = scps[i];
1671       const struct spvdx_style *interval = NULL;
1672       const struct spvdx_style *graph = NULL;
1673       const struct spvdx_style *labeling = NULL;
1674       const struct spvdx_style *frame = NULL;
1675       const struct spvdx_style *major_ticks = NULL;
1676       const struct spvdx_set_format *set_format = NULL;
1677       for (size_t j = 0; j < scp->n_seq; j++)
1678         {
1679           const struct spvxml_node *node = scp->seq[j];
1680           if (spvdx_is_set_style (node))
1681             {
1682               const struct spvdx_set_style *set_style
1683                 = spvdx_cast_set_style (node);
1684               if (spvdx_is_graph (set_style->target))
1685                 graph = set_style->style;
1686               else if (spvdx_is_labeling (set_style->target))
1687                 labeling = set_style->style;
1688               else if (spvdx_is_interval (set_style->target))
1689                 interval = set_style->style;
1690               else if (spvdx_is_major_ticks (set_style->target))
1691                 major_ticks = set_style->style;
1692               else
1693                 NOT_REACHED ();
1694             }
1695           else if (spvdx_is_set_frame_style (node))
1696             frame = spvdx_cast_set_frame_style (node)->style;
1697           else if (spvdx_is_set_format (node))
1698             set_format = spvdx_cast_set_format (node);
1699           else
1700             assert (spvdx_is_set_meta_data (node));
1701         }
1702
1703       if (scp->union_ && scp->apply_to_converse <= 0)
1704         {
1705           for (size_t j = 0; j < scp->union_->n_intersect; j++)
1706             decode_set_cell_properties__ (
1707               table, series_map, scp->union_->intersect[j],
1708               interval, graph, labeling, frame, major_ticks, set_format);
1709         }
1710       else if (!scp->union_ && scp->apply_to_converse > 0)
1711         {
1712           if ((set_format && spvdx_is_labeling (set_format->target))
1713               || labeling || interval)
1714             {
1715               struct pivot_cell *cell;
1716               HMAP_FOR_EACH (cell, struct pivot_cell, hmap_node, &table->cells)
1717                 apply_styles_to_value (table, cell->value, set_format,
1718                                        &table->areas[PIVOT_AREA_DATA],
1719                                        NULL, NULL);
1720             }
1721         }
1722       else if (!scp->union_ && scp->apply_to_converse <= 0)
1723         {
1724           /* Appears to be used to set the font for something--but what? */
1725         }
1726       else
1727         NOT_REACHED ();
1728     }
1729 }
1730
1731 char * WARN_UNUSED_RESULT
1732 decode_spvsx_legacy_properties (const struct spvsx_table_properties *in,
1733                                 struct spv_legacy_properties **outp)
1734 {
1735   struct spv_legacy_properties *out = xzalloc (sizeof *out);
1736   char *error;
1737
1738   if (!in)
1739     {
1740       error = xstrdup ("Legacy table lacks tableProperties");
1741       goto error;
1742     }
1743
1744   const struct spvsx_general_properties *g = in->general_properties;
1745   out->omit_empty = g->hide_empty_rows != 0;
1746   out->width_ranges[TABLE_HORZ][0] = optional_pt (g->minimum_column_width, -1);
1747   out->width_ranges[TABLE_HORZ][1] = optional_pt (g->maximum_column_width, -1);
1748   out->width_ranges[TABLE_VERT][0] = optional_pt (g->minimum_row_width, -1);
1749   out->width_ranges[TABLE_VERT][1] = optional_pt (g->maximum_row_width, -1);
1750   out->row_labels_in_corner
1751     = g->row_dimension_labels != SPVSX_ROW_DIMENSION_LABELS_NESTED;
1752
1753   const struct spvsx_footnote_properties *f = in->footnote_properties;
1754   out->footnote_marker_superscripts
1755     = (f->marker_position != SPVSX_MARKER_POSITION_SUBSCRIPT);
1756   out->show_numeric_markers
1757     = (f->number_format == SPVSX_NUMBER_FORMAT_NUMERIC);
1758
1759   for (int i = 0; i < PIVOT_N_AREAS; i++)
1760     pivot_area_get_default_style (i, &out->areas[i]);
1761
1762   const struct spvsx_cell_format_properties *cfp = in->cell_format_properties;
1763   for (size_t i = 0; i < cfp->n_cell_style; i++)
1764     {
1765       const struct spvsx_cell_style *c = cfp->cell_style[i];
1766       const char *name = CHAR_CAST (const char *, c->node_.raw->name);
1767       enum pivot_area area = pivot_area_from_name (name);
1768       if (area == PIVOT_N_AREAS)
1769         {
1770           error = xasprintf ("unknown area \"%s\" in cellFormatProperties",
1771                              name);
1772           goto error;
1773         }
1774
1775       struct area_style *a = &out->areas[area];
1776       const struct spvsx_style *s = c->style;
1777       if (s->font_weight)
1778         a->font_style.bold = s->font_weight == SPVSX_FONT_WEIGHT_BOLD;
1779       if (s->font_style)
1780         a->font_style.italic = s->font_style == SPVSX_FONT_STYLE_ITALIC;
1781       a->font_style.underline = false;
1782       if (s->color >= 0)
1783         a->font_style.fg[0] = optional_color (
1784           s->color, (struct cell_color) CELL_COLOR_BLACK);
1785       if (c->alternating_text_color >= 0 || s->color >= 0)
1786         a->font_style.fg[1] = optional_color (c->alternating_text_color,
1787                                               a->font_style.fg[0]);
1788       if (s->color2 >= 0)
1789         a->font_style.bg[0] = optional_color (
1790           s->color2, (struct cell_color) CELL_COLOR_WHITE);
1791       if (c->alternating_color >= 0 || s->color2 >= 0)
1792         a->font_style.bg[1] = optional_color (c->alternating_color,
1793                                               a->font_style.bg[0]);
1794       if (s->font_family)
1795         {
1796           free (a->font_style.typeface);
1797           a->font_style.typeface = xstrdup (s->font_family);
1798         }
1799
1800       if (s->font_size)
1801         a->font_style.size = optional_length (s->font_size, 0);
1802
1803       if (s->text_alignment)
1804         a->cell_style.halign
1805           = (s->text_alignment == SPVSX_TEXT_ALIGNMENT_LEFT
1806              ? TABLE_HALIGN_LEFT
1807              : s->text_alignment == SPVSX_TEXT_ALIGNMENT_RIGHT
1808              ? TABLE_HALIGN_RIGHT
1809              : s->text_alignment == SPVSX_TEXT_ALIGNMENT_CENTER
1810              ? TABLE_HALIGN_CENTER
1811              : s->text_alignment == SPVSX_TEXT_ALIGNMENT_DECIMAL
1812              ? TABLE_HALIGN_DECIMAL
1813              : TABLE_HALIGN_MIXED);
1814       if (s->label_location_vertical)
1815         a->cell_style.valign
1816           = (s->label_location_vertical == SPVSX_LABEL_LOCATION_VERTICAL_NEGATIVE
1817              ? TABLE_VALIGN_BOTTOM
1818              : s->label_location_vertical == SPVSX_LABEL_LOCATION_VERTICAL_POSITIVE
1819              ? TABLE_VALIGN_TOP
1820              : TABLE_VALIGN_CENTER);
1821
1822       if (s->decimal_offset != DBL_MAX)
1823         a->cell_style.decimal_offset = optional_px (s->decimal_offset, 0);
1824
1825       if (s->margin_left != DBL_MAX)
1826         a->cell_style.margin[TABLE_HORZ][0] = optional_px (s->margin_left, 8);
1827       if (s->margin_right != DBL_MAX)
1828         a->cell_style.margin[TABLE_HORZ][1] = optional_px (s->margin_right,
1829                                                            11);
1830       if (s->margin_top != DBL_MAX)
1831         a->cell_style.margin[TABLE_VERT][0] = optional_px (s->margin_top, 1);
1832       if (s->margin_bottom != DBL_MAX)
1833         a->cell_style.margin[TABLE_VERT][1] = optional_px (s->margin_bottom,
1834                                                            1);
1835     }
1836
1837   for (int i = 0; i < PIVOT_N_BORDERS; i++)
1838     pivot_border_get_default_style (i, &out->borders[i]);
1839
1840   const struct spvsx_border_properties *bp = in->border_properties;
1841   for (size_t i = 0; i < bp->n_border_style; i++)
1842     {
1843       const struct spvsx_border_style *bin = bp->border_style[i];
1844       const char *name = CHAR_CAST (const char *, bin->node_.raw->name);
1845       enum pivot_border border = pivot_border_from_name (name);
1846       if (border == PIVOT_N_BORDERS)
1847         {
1848           error = xasprintf ("unknown border \"%s\" parsing borderProperties",
1849                              name);
1850           goto error;
1851         }
1852
1853       struct table_border_style *bout = &out->borders[border];
1854       bout->stroke
1855         = (bin->border_style_type == SPVSX_BORDER_STYLE_TYPE_NONE
1856            ? TABLE_STROKE_NONE
1857            : bin->border_style_type == SPVSX_BORDER_STYLE_TYPE_DASHED
1858            ? TABLE_STROKE_DASHED
1859            : bin->border_style_type == SPVSX_BORDER_STYLE_TYPE_THICK
1860            ? TABLE_STROKE_THICK
1861            : bin->border_style_type == SPVSX_BORDER_STYLE_TYPE_THIN
1862            ? TABLE_STROKE_THIN
1863            : bin->border_style_type == SPVSX_BORDER_STYLE_TYPE_DOUBLE
1864            ? TABLE_STROKE_DOUBLE
1865            : TABLE_STROKE_SOLID);
1866       bout->color = optional_color (bin->color,
1867                                     (struct cell_color) CELL_COLOR_BLACK);
1868     }
1869
1870   const struct spvsx_printing_properties *pp = in->printing_properties;
1871   out->print_all_layers = pp->print_all_layers > 0;
1872   out->paginate_layers = pp->print_each_layer_on_separate_page > 0;
1873   out->shrink_to_width = pp->rescale_wide_table_to_fit_page > 0;
1874   out->shrink_to_length = pp->rescale_long_table_to_fit_page > 0;
1875   out->top_continuation = pp->continuation_text_at_top > 0;
1876   out->bottom_continuation = pp->continuation_text_at_bottom > 0;
1877   out->continuation = xstrdup (pp->continuation_text
1878                                ? pp->continuation_text : "(cont.)");
1879   out->n_orphan_lines = optional_int (pp->window_orphan_lines, 2);
1880
1881   *outp = out;
1882   return NULL;
1883
1884 error:
1885   spv_legacy_properties_destroy (out);
1886   *outp = NULL;
1887   return error;
1888 }
1889
1890 void
1891 spv_legacy_properties_destroy (struct spv_legacy_properties *props)
1892 {
1893   if (props)
1894     {
1895       for (size_t i = 0; i < PIVOT_N_AREAS; i++)
1896         area_style_uninit (&props->areas[i]);
1897       free (props->continuation);
1898       free (props);
1899     }
1900 }
1901
1902 static struct spv_series *
1903 parse_formatting (const struct spvdx_visualization *v,
1904                   const struct hmap *series_map, struct hmap *format_map)
1905 {
1906   const struct spvdx_labeling *labeling = v->graph->interval->labeling;
1907   struct spv_series *cell_format = NULL;
1908   for (size_t i = 0; i < labeling->n_seq; i++)
1909     {
1910       const struct spvdx_formatting *f
1911         = spvdx_cast_formatting (labeling->seq[i]);
1912       if (!f)
1913         continue;
1914
1915       cell_format = spv_series_from_ref (series_map, f->variable);
1916       for (size_t j = 0; j < f->n_format_mapping; j++)
1917         {
1918           const struct spvdx_format_mapping *fm = f->format_mapping[j];
1919
1920           if (fm->format)
1921             {
1922               struct format_mapping *out = xmalloc (sizeof *out);
1923               out->from = fm->from;
1924               out->to = decode_format (fm->format);
1925               hmap_insert (format_map, &out->hmap_node,
1926                            hash_int (out->from, 0));
1927             }
1928         }
1929     }
1930
1931   return cell_format;
1932 }
1933
1934 static void
1935 format_map_destroy (struct hmap *format_map)
1936 {
1937   struct format_mapping *fm, *next;
1938   HMAP_FOR_EACH_SAFE (fm, next, struct format_mapping, hmap_node, format_map)
1939     {
1940       hmap_delete (format_map, &fm->hmap_node);
1941       free (fm);
1942     }
1943   hmap_destroy (format_map);
1944 }
1945
1946 char * WARN_UNUSED_RESULT
1947 decode_spvdx_table (const struct spvdx_visualization *v,
1948                     const struct spv_legacy_properties *props,
1949                     struct spv_data *data, struct pivot_table **outp)
1950 {
1951   struct pivot_table *table = pivot_table_create__ (NULL);
1952
1953   struct hmap series_map = HMAP_INITIALIZER (series_map);
1954   struct hmap format_map = HMAP_INITIALIZER (format_map);
1955   struct spv_series **dim_series = NULL;
1956   char *error;
1957
1958   /* First get the legacy properties. */
1959   table->omit_empty = props->omit_empty;
1960   for (enum table_axis axis = 0; axis < TABLE_N_AXES; axis++)
1961     for (int i = 0; i < 2; i++)
1962       if (props->width_ranges[axis][i] > 0)
1963         table->sizing[axis].range[i] = props->width_ranges[axis][i];
1964   table->row_labels_in_corner = props->row_labels_in_corner;
1965
1966   table->footnote_marker_superscripts = props->footnote_marker_superscripts;
1967   table->show_numeric_markers = props->show_numeric_markers;
1968
1969   for (size_t i = 0; i < PIVOT_N_AREAS; i++)
1970     {
1971       area_style_uninit (&table->areas[i]);
1972       area_style_copy (&table->areas[i], &props->areas[i]);
1973     }
1974   for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
1975     table->borders[i] = props->borders[i];
1976
1977   table->print_all_layers = props->print_all_layers;
1978   table->paginate_layers = props->paginate_layers;
1979   table->shrink_to_fit[TABLE_HORZ] = props->shrink_to_width;
1980   table->shrink_to_fit[TABLE_VERT] = props->shrink_to_length;
1981   table->top_continuation = props->top_continuation;
1982   table->bottom_continuation = props->bottom_continuation;
1983   table->continuation = xstrdup (props->continuation);
1984   table->n_orphan_lines = props->n_orphan_lines;
1985
1986   struct spvdx_visualization_extension *ve = v->visualization_extension;
1987   table->show_grid_lines = ve && ve->show_gridline;
1988
1989   /* Sizing from the legacy properties can get overridden. */
1990   if (v->graph->cell_style->width)
1991     {
1992       int min_width, max_width, n = 0;
1993       if (sscanf (v->graph->cell_style->width, "%*d%%;%dpt;%dpt%n",
1994                   &min_width, &max_width, &n)
1995           && v->graph->cell_style->width[n] == '\0')
1996         {
1997           table->sizing[TABLE_HORZ].range[0] = min_width;
1998           table->sizing[TABLE_HORZ].range[1] = max_width;
1999         }
2000     }
2001
2002   /* Footnotes.
2003
2004      Any pivot_value might refer to footnotes, so it's important to process the
2005      footnotes early to ensure that those references can be resolved.  There is
2006      a possible problem that a footnote might itself reference an
2007      as-yet-unprocessed footnote, but that's OK because footnote references
2008      don't actually look at the footnote contents but only resolve a pointer to
2009      where the footnote will go later.
2010
2011      Before we really start, create all the footnotes we'll fill in.  This is
2012      because sometimes footnotes refer to themselves or to each other and we
2013      don't want to reject those references. */
2014   if (v->container)
2015     for (size_t i = 0; i < v->container->n_label_frame; i++)
2016       {
2017         const struct spvdx_label_frame *lf = v->container->label_frame[i];
2018         if (lf->label
2019             && lf->label->purpose == SPVDX_PURPOSE_FOOTNOTE
2020             && lf->label->n_text > 0
2021             && lf->label->text[0]->uses_reference > 0)
2022           {
2023             pivot_table_create_footnote__ (
2024               table, lf->label->text[0]->uses_reference - 1,
2025               NULL, NULL);
2026           }
2027       }
2028
2029   if (v->graph->interval->footnotes)
2030     decode_footnotes (table, v->graph->interval->footnotes);
2031
2032   struct spv_series *footnotes = NULL;
2033   for (size_t i = 0; i < v->graph->interval->labeling->n_seq; i++)
2034     {
2035       const struct spvxml_node *node = v->graph->interval->labeling->seq[i];
2036       if (spvdx_is_footnotes (node))
2037         {
2038           const struct spvdx_footnotes *f = spvdx_cast_footnotes (node);
2039           footnotes = spv_series_from_ref (&series_map, f->variable);
2040           decode_footnotes (table, f);
2041         }
2042     }
2043   for (size_t i = 0; i < v->n_lf1; i++)
2044     {
2045       error = decode_label_frame (table, v->lf1[i]);
2046       if (error)
2047         goto exit;
2048     }
2049   for (size_t i = 0; i < v->n_lf2; i++)
2050     {
2051       error = decode_label_frame (table, v->lf2[i]);
2052       if (error)
2053         goto exit;
2054     }
2055   if (v->container)
2056     for (size_t i = 0; i < v->container->n_label_frame; i++)
2057       {
2058         error = decode_label_frame (table, v->container->label_frame[i]);
2059         if (error)
2060           goto exit;
2061       }
2062   if (v->graph->interval->labeling->style)
2063     {
2064       area_style_uninit (&table->areas[PIVOT_AREA_DATA]);
2065       decode_spvdx_style (v->graph->interval->labeling->style,
2066                           v->graph->cell_style,
2067                           &table->areas[PIVOT_AREA_DATA]);
2068     }
2069
2070   /* Decode all of the sourceVariable and derivedVariable  */
2071   struct spvxml_node **nodes = xmemdup (v->seq, v->n_seq * sizeof *v->seq);
2072   size_t n_nodes = v->n_seq;
2073   while (n_nodes > 0)
2074     {
2075       bool progress = false;
2076       for (size_t i = 0; i < n_nodes; )
2077         {
2078           error = (spvdx_is_source_variable (nodes[i])
2079                    ? decode_spvdx_source_variable (nodes[i], data, &series_map)
2080                    : decode_spvdx_derived_variable (nodes[i], &series_map));
2081           if (!error)
2082             {
2083               nodes[i] = nodes[--n_nodes];
2084               progress = true;
2085             }
2086           else if (error == &BAD_REFERENCE)
2087             i++;
2088           else
2089             {
2090               free (nodes);
2091               goto exit;
2092             }
2093         }
2094
2095       if (!progress)
2096         {
2097           free (nodes);
2098           error = xasprintf ("Table has %zu variables with circular or "
2099                              "unresolved references, including variable %s.",
2100                              n_nodes, nodes[0]->id);
2101           goto exit;
2102         }
2103     }
2104   free (nodes);
2105
2106   const struct spvdx_cross *cross = v->graph->faceting->cross;
2107
2108   assert (cross->n_seq == 1);
2109   const struct spvdx_nest *columns = spvdx_cast_nest (cross->seq[0]);
2110   size_t max_columns = columns ? columns->n_vars : 0;
2111
2112   assert (cross->n_seq2 == 1);
2113   const struct spvdx_nest *rows = spvdx_cast_nest (cross->seq2[0]);
2114   size_t max_rows = rows ? rows->n_vars : 0;
2115
2116   size_t max_layers = (v->graph->faceting->n_layers1
2117                        + v->graph->faceting->n_layers2);
2118
2119   size_t max_dims = max_columns + max_rows + max_layers;
2120   table->dimensions = xnmalloc (max_dims, sizeof *table->dimensions);
2121   dim_series = xnmalloc (max_dims, sizeof *dim_series);
2122   size_t n_dim_series = 0;
2123   add_dimensions (&series_map, columns, PIVOT_AXIS_COLUMN, v, table,
2124                   dim_series, &n_dim_series, 1);
2125   add_dimensions (&series_map, rows, PIVOT_AXIS_ROW, v, table,
2126                   dim_series, &n_dim_series, max_columns + 1);
2127   add_layers (&series_map,
2128               v->graph->faceting->layers1, v->graph->faceting->n_layers1,
2129               v, table, dim_series, &n_dim_series, max_rows + max_columns + 1);
2130   add_layers (&series_map,
2131               v->graph->faceting->layers2, v->graph->faceting->n_layers2,
2132               v, table, dim_series, &n_dim_series,
2133               max_rows + max_columns + v->graph->faceting->n_layers1 + 1);
2134
2135   struct spv_series *cell = spv_series_find (&series_map, "cell");
2136   if (!cell)
2137     {
2138       error = xstrdup (_("Table lacks cell data."));
2139       goto exit;
2140     }
2141
2142   struct spv_series *cell_format = parse_formatting (v, &series_map,
2143                                                      &format_map);
2144
2145   assert (table->n_dimensions == n_dim_series);
2146   size_t *dim_indexes = xnmalloc (table->n_dimensions, sizeof *dim_indexes);
2147   for (size_t i = 0; i < cell->n_values; i++)
2148     {
2149       for (size_t j = 0; j < table->n_dimensions; j++)
2150         {
2151           const struct spv_data_value *value = &dim_series[j]->values[i];
2152           const struct pivot_category *cat = find_category (
2153             dim_series[j], value->width < 0 ? value->d : value->index);
2154           if (!cat)
2155             goto skip;
2156           dim_indexes[j] = cat->data_index;
2157         }
2158
2159       struct pivot_value *value = pivot_value_from_data_value (
2160         &cell->values[i], cell_format ? &cell_format->values[i] : NULL,
2161         &format_map);
2162       if (footnotes)
2163         {
2164           const struct spv_data_value *d = &footnotes->values[i];
2165           if (d->width >= 0)
2166             {
2167               const char *p = d->s;
2168               while (*p)
2169                 {
2170                   char *tail;
2171                   int idx = strtol (p, &tail, 10);
2172                   add_footnote (value, idx, table);
2173                   if (tail == p)
2174                     break;
2175                   p = tail;
2176                   if (*p == ',')
2177                     p++;
2178                 }
2179             }
2180         }
2181
2182       if (value->type == PIVOT_VALUE_NUMERIC
2183           && value->numeric.x == SYSMIS
2184           && !value->n_footnotes)
2185         {
2186           /* Apparently, system-missing values are just empty cells? */
2187           pivot_value_destroy (value);
2188         }
2189       else
2190         pivot_table_put (table, dim_indexes, table->n_dimensions, value);
2191     skip:;
2192     }
2193   free (dim_indexes);
2194
2195   decode_set_cell_properties (table, &series_map, v->graph->facet_layout->scp1,
2196                               v->graph->facet_layout->n_scp1);
2197   decode_set_cell_properties (table, &series_map, v->graph->facet_layout->scp2,
2198                               v->graph->facet_layout->n_scp2);
2199
2200   pivot_table_assign_label_depth (table);
2201
2202   format_map_destroy (&format_map);
2203
2204 exit:
2205   free (dim_series);
2206   spv_series_destroy (&series_map);
2207   if (error)
2208     {
2209       pivot_table_unref (table);
2210       *outp = NULL;
2211     }
2212   else
2213     *outp = table;
2214   return error;
2215 }