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