1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2017, 2018 Free Software Foundation, Inc.
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.
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.
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/>. */
19 #include "output/spv/spv-legacy-decoder.h"
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"
41 #include "gl/c-strtod.h"
42 #include "gl/xalloc.h"
43 #include "gl/xmemdup0.h"
45 #include <libxml/tree.h>
48 #define N_(msgid) msgid
49 #define _(msgid) gettext (msgid)
51 struct spv_legacy_properties
53 /* General properties. */
55 int width_ranges[TABLE_N_AXES][2]; /* In 1/96" units. */
56 bool row_labels_in_corner;
58 /* Footnote display settings. */
59 bool show_numeric_markers;
60 bool footnote_marker_superscripts;
63 struct area_style areas[PIVOT_N_AREAS];
64 struct table_border_style borders[PIVOT_N_BORDERS];
67 bool print_all_layers;
70 bool shrink_to_length;
71 bool top_continuation, bottom_continuation;
73 size_t n_orphan_lines;
78 struct hmap_node hmap_node; /* By name. */
81 struct fmt_spec format;
83 struct spv_series *label_series;
86 const struct spvxml_node *xml;
88 struct spv_data_value *values;
90 struct hmap map; /* Contains "struct spv_mapping". */
93 struct pivot_dimension *dimension;
95 struct pivot_category **index_to_category;
98 struct spvdx_affix **affixes;
102 static void spv_map_destroy (struct hmap *);
104 static struct spv_series *
105 spv_series_first (struct hmap *series_map)
107 struct spv_series *series;
108 HMAP_FOR_EACH (series, struct spv_series, hmap_node, series_map)
113 static struct spv_series *
114 spv_series_find (const struct hmap *series_map, const char *name)
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))
124 static struct spv_series *
125 spv_series_from_ref (const struct hmap *series_map,
126 const struct spvxml_node *ref)
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);
134 printf ("missing series %s\n", node->id);
139 spv_series_dump (const struct spv_series *series)
141 printf ("series \"%s\"", series->name);
143 printf (" (label \"%s\")", series->label);
144 printf (", %zu values:", series->n_values);
145 for (size_t i = 0; i < series->n_values; i++)
148 spv_data_value_dump (&series->values[i], stdout);
154 spv_series_destroy (struct hmap *series_map)
156 struct spv_series *series, *next_series;
157 HMAP_FOR_EACH_SAFE (series, next_series, struct spv_series, hmap_node,
161 free (series->label);
163 for (size_t i = 0; i < series->n_values; i++)
164 spv_data_value_uninit (&series->values[i]);
165 free (series->values);
167 spv_map_destroy (&series->map);
169 free (series->index_to_category);
171 hmap_delete (series_map, &series->hmap_node);
174 hmap_destroy (series_map);
179 struct hmap_node hmap_node;
181 struct spv_data_value to;
184 static struct spv_mapping *
185 spv_map_search (const struct hmap *map, double from)
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)
195 static const struct spv_data_value *
196 spv_map_lookup (const struct hmap *map, const struct spv_data_value *in)
201 const struct spv_mapping *m = spv_map_search (map, in->d);
202 return m ? &m->to : in;
206 parse_real (const char *s, double *real)
208 int save_errno = errno;
211 *real = c_strtod (s, &end);
212 bool ok = !errno && end > s && !*end;
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)
222 struct spv_mapping *mapping = spv_map_search (map, from);
225 return xasprintf ("Duplicate relabeling for from=\"%.*g\"",
227 mapping = xmalloc (sizeof *mapping);
228 mapping->from = from;
230 if ((try_strings_as_numbers || (format && fmt_is_numeric (format->type)))
231 && parse_real (to, &mapping->to.d))
233 if (try_strings_as_numbers)
234 mapping->to.width = -1;
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);
244 mapping->to.width = strlen (to);
245 mapping->to.s = xstrdup (to);
247 hmap_insert (map, &mapping->hmap_node, hash_double (from, 0));
252 spv_map_destroy (struct hmap *map)
254 struct spv_mapping *mapping, *next;
255 HMAP_FOR_EACH_SAFE (mapping, next, struct spv_mapping, hmap_node, map)
257 spv_data_value_uninit (&mapping->to);
258 hmap_delete (map, &mapping->hmap_node);
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)
270 for (size_t i = 0; i < n_relabels; i++)
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);
281 static char * WARN_UNUSED_RESULT
282 spv_series_parse_value_map_entry (struct hmap *map,
283 const struct spvdx_value_map_entry *vme)
285 for (const char *p = vme->from; ; p++)
287 int save_errno = errno;
290 double from = c_strtod (p, &end);
291 bool ok = !errno && end > p && strchr (";", *end);
294 return xasprintf ("Syntax error in valueMapEntry from=\"%s\".",
297 char *error = spv_map_insert (map, from, vme->to, true,
298 &(struct fmt_spec) { FMT_A, 40, 0 });
309 static struct fmt_spec
310 decode_date_time_format (const struct spvdx_date_time_format *dtf)
312 if (dtf->dt_base_format == SPVDX_DT_BASE_FORMAT_DATE)
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
324 int w = fmt_min_output_width (type);
325 if (dtf->year_abbreviation <= 0)
327 return (struct fmt_spec) { .type = type, .w = w };
332 = (dtf->dt_base_format == SPVDX_DT_BASE_FORMAT_DATE_TIME
333 ? (dtf->mdy_order == SPVDX_MDY_ORDER_YEAR_MONTH_DAY
336 : (dtf->show_day > 0 ? FMT_DTIME
337 : dtf->show_hour > 0 ? FMT_TIME
339 int w = fmt_min_output_width (type);
341 if (dtf->show_second > 0)
344 if (dtf->show_millis > 0)
350 return (struct fmt_spec) { .type = type, .w = w, .d = d };
354 static struct fmt_spec
355 decode_elapsed_time_format (const struct spvdx_elapsed_time_format *etf)
358 = (etf->dt_base_format != SPVDX_DT_BASE_FORMAT_TIME ? FMT_DTIME
359 : etf->show_hour > 0 ? FMT_TIME
361 int w = fmt_min_output_width (type);
363 if (etf->show_second > 0)
366 if (etf->show_millis > 0)
372 return (struct fmt_spec) { .type = type, .w = w, .d = d };
375 static struct fmt_spec
376 decode_number_format (const struct spvdx_number_format *nf)
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
384 int d = nf->maximum_fraction_digits;
388 struct fmt_spec f = (struct fmt_spec) { type, 40, d };
393 /* Returns an *approximation* of IN as a fmt_spec.
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)
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)
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,
436 return decode_date_time_format (&dtf);
438 else if (in->f_base_format == SPVDX_F_BASE_FORMAT_ELAPSED_TIME)
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,
456 return decode_elapsed_time_format (&etf);
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,
468 .prefix = in->prefix,
469 .suffix = in->suffix,
471 return decode_number_format (&nf);
476 spv_series_execute_mapping (struct spv_series *series)
478 if (!hmap_is_empty (&series->map))
480 series->remapped = true;
481 for (size_t i = 0; i < series->n_values; i++)
483 struct spv_data_value *value = &series->values[i];
484 if (value->width >= 0)
487 const struct spv_mapping *mapping = spv_map_search (&series->map,
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);
497 value->d = mapping->to.d;
503 static char * WARN_UNUSED_RESULT
504 spv_series_remap_formats (struct spv_series *series,
505 struct spvxml_node **seq, size_t n_seq)
507 spv_map_destroy (&series->map);
508 hmap_init (&series->map);
509 for (size_t i = 0; i < n_seq; i++)
511 struct spvxml_node *node = seq[i];
512 if (spvdx_is_format (node))
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);
522 series->affixes = f->affix;
523 series->n_affixes = f->n_affix;
525 else if (spvdx_is_string_format (node))
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,
534 series->affixes = sf->affix;
535 series->n_affixes = sf->n_affix;
540 spv_series_execute_mapping (series);
544 static char * WARN_UNUSED_RESULT
545 spv_series_remap_vmes (struct spv_series *series,
546 struct spvdx_value_map_entry **vmes,
549 spv_map_destroy (&series->map);
550 hmap_init (&series->map);
551 for (size_t i = 0; i < n_vmes; i++)
553 char *error = spv_series_parse_value_map_entry (&series->map, vmes[i]);
557 spv_series_execute_mapping (series);
562 decode_footnotes (struct pivot_table *table, const struct spvdx_footnotes *f)
564 if (f->n_footnote_mapping > 0)
565 pivot_table_create_footnote__ (table, f->n_footnote_mapping - 1,
567 for (size_t i = 0; i < f->n_footnote_mapping; i++)
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),
576 static struct cell_color
577 optional_color (int color, struct cell_color default_color)
580 ? (struct cell_color) CELL_COLOR (color >> 16, color >> 8, color)
585 optional_length (const char *s, int default_length)
587 /* There is usually a "pt" suffix. We ignore it. */
589 return s && sscanf (s, "%d", &length) == 1 ? length : default_length;
593 optional_px (double inches, int default_px)
595 return inches != DBL_MAX ? inches * 96.0 : default_px;
599 optional_pt (double inches, int default_pt)
601 return inches != DBL_MAX ? inches * 72.0 + .5 : default_pt;
605 decode_spvdx_style_incremental (const struct spvdx_style *in,
606 const struct spvdx_style *bg,
607 struct area_style *out)
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)
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];
621 if (bg && bg->color >= 0)
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];
627 if (in && in->font_family)
629 free (out->font_style.typeface);
630 out->font_style.typeface = xstrdup (in->font_family);
632 if (in && in->font_size)
634 int size = optional_length (in->font_size, 0);
636 out->font_style.size = size;
638 if (in && in->text_alignment)
639 out->cell_style.halign
640 = (in->text_alignment == SPVDX_TEXT_ALIGNMENT_LEFT
642 : in->text_alignment == SPVDX_TEXT_ALIGNMENT_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
655 : TABLE_VALIGN_CENTER);
656 if (in && in->decimal_offset != DBL_MAX)
657 out->cell_style.decimal_offset = optional_px (in->decimal_offset, 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);
671 decode_spvdx_style (const struct spvdx_style *in,
672 const struct spvdx_style *bg,
673 struct area_style *out)
675 *out = (struct area_style) AREA_STYLE_INITIALIZER;
676 decode_spvdx_style_incremental (in, bg, out);
680 add_footnote (struct pivot_value *v, int idx, struct pivot_table *table)
682 if (idx < 1 || idx > table->n_footnotes)
685 pivot_value_add_footnote (v, table->footnotes[idx - 1]);
688 static char * WARN_UNUSED_RESULT
689 decode_label_frame (struct pivot_table *table,
690 const struct spvdx_label_frame *lf)
695 struct pivot_value **target;
696 struct area_style *area;
697 if (lf->label->purpose == SPVDX_PURPOSE_TITLE)
699 target = &table->title;
700 area = &table->areas[PIVOT_AREA_TITLE];
702 else if (lf->label->purpose == SPVDX_PURPOSE_SUB_TITLE)
704 target = &table->caption;
705 area = &table->areas[PIVOT_AREA_CAPTION];
707 else if (lf->label->purpose == SPVDX_PURPOSE_FOOTNOTE)
709 if (lf->label->n_text > 0
710 && lf->label->text[0]->uses_reference != INT_MIN)
713 area = &table->areas[PIVOT_AREA_FOOTER];
718 else if (lf->label->purpose == SPVDX_PURPOSE_LAYER)
721 area = &table->areas[PIVOT_AREA_LAYERS];
726 area_style_uninit (area);
727 decode_spvdx_style (lf->label->style, lf->label->text_frame_style, area);
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++)
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);
742 char *new = xasprintf ("%s%s", value->text.local, in->text);
743 free (value->text.local);
744 value->text.local = new;
747 pivot_value_destroy (*target);
751 for (size_t i = 0; i < lf->label->n_text; i++)
753 const struct spvdx_text *in = lf->label->text[i];
754 if (in->uses_reference == INT_MIN)
758 size_t length = strlen (in->text);
759 if (length && in->text[length - 1] == '\n')
762 pivot_table_create_footnote__ (
763 table, in->uses_reference - 1, NULL,
764 pivot_value_new_user_text (in->text, length));
768 size_t length = strlen (in->text);
769 if (length && in->text[length - 1] == '.')
772 pivot_table_create_footnote__ (
773 table, in->uses_reference - 1,
774 pivot_value_new_user_text (in->text, length), NULL);
780 /* Special return value for decode_spvdx_variable(). */
781 static char BAD_REFERENCE;
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)
788 const struct spvdx_source_variable *sv = spvdx_cast_source_variable (node);
790 struct spv_series *label_series = NULL;
791 if (sv->label_variable)
793 label_series = spv_series_find (series_map,
794 sv->label_variable->node_.id);
796 return &BAD_REFERENCE;
798 label_series->is_label_series = true;
801 const struct spv_data_variable *var = spv_data_find_variable (
802 data, sv->source, sv->source_name);
804 return xasprintf ("sourceVariable %s references nonexistent "
805 "source %s variable %s.",
806 sv->node_.id, sv->source, sv->source_name);
808 struct spv_series *s = xzalloc (sizeof *s);
809 s->name = xstrdup (node->id);
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;
817 hmap_insert (series_map, &s->hmap_node, hash_string (s->name, 0));
819 char *error = spv_series_remap_formats (s, sv->seq, sv->n_seq);
823 if (label_series && !s->remapped)
825 for (size_t i = 0; i < s->n_values; i++)
826 if (s->values[i].width < 0)
829 if (label_series->values[i].width < 0)
831 union value v = { .f = label_series->values[i].d };
832 dest = data_out_stretchy (&v, "UTF-8", &s->format, NULL);
835 dest = label_series->values[i].s;
836 char *error = spv_map_insert (&s->map, s->values[i].d,
838 free (error); /* Duplicates are OK. */
839 if (label_series->values[i].width < 0)
847 static char * WARN_UNUSED_RESULT
848 decode_spvdx_derived_variable (const struct spvxml_node *node,
849 struct hmap *series_map)
851 const struct spvdx_derived_variable *dv = spvdx_cast_derived_variable (node);
853 struct spv_data_value *values;
856 struct substring value = ss_cstr (dv->value);
857 if (ss_equals (value, ss_cstr ("constant(0)")))
859 struct spv_series *existing_series = spv_series_first (series_map);
860 if (!existing_series)
861 return &BAD_REFERENCE;
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;
868 else if (ss_starts_with (value, ss_cstr ("constant(")))
873 else if (ss_starts_with (value, ss_cstr ("map("))
874 && ss_ends_with (value, ss_cstr (")")))
876 char *dependency_name = ss_xstrdup (ss_substr (value, 4,
878 struct spv_series *dependency
879 = spv_series_find (series_map, dependency_name);
880 free (dependency_name);
882 return &BAD_REFERENCE;
884 values = spv_data_values_clone (dependency->values,
885 dependency->n_values);
886 n_values = dependency->n_values;
889 return xasprintf ("Derived variable %s has unknown value \"%s\"",
890 node->id, dv->value);
892 struct spv_series *s = xzalloc (sizeof *s);
894 s->name = xstrdup (node->id);
896 s->n_values = n_values;
898 hmap_insert (series_map, &s->hmap_node, hash_string (s->name, 0));
900 char *error = spv_series_remap_vmes (s, dv->value_map_entry,
901 dv->n_value_map_entry);
905 error = spv_series_remap_formats (s, dv->seq, dv->n_seq);
911 for (size_t i = 0; i < n_values; i++)
912 if (values[i].width != 0)
914 for (size_t i = 0; i < n_values; i++)
915 spv_data_value_uninit (&s->values[i]);
926 struct format_mapping
928 struct hmap_node hmap_node;
933 static const struct format_mapping *
934 format_map_find (const struct hmap *format_map, uint32_t u32_format)
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)
948 static struct fmt_spec
949 spv_format_from_data_value (const struct spv_data_value *data,
950 const struct hmap *format_map)
953 return fmt_for_output (FMT_F, 40, 2);
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);
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)
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)
969 if (format && fmt_get_category (f.type) == FMT_CAT_DATE)
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,
976 && data->s[len] == '\0')
978 double date = calendar_gregorian_to_offset (year, month, day,
982 v->type = PIVOT_VALUE_NUMERIC;
983 v->numeric.x = (date * 60. * 60. * 24.
988 v->numeric.format = f;
993 else if (format && fmt_get_category (f.type) == FMT_CAT_TIME)
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
999 && data->s[len] == '\0')
1001 v->type = PIVOT_VALUE_NUMERIC;
1002 v->numeric.x = (hour * 60. * 60.
1006 v->numeric.format = f;
1010 v->type = PIVOT_VALUE_STRING;
1011 v->string.s = xstrdup (data->s);
1015 v->type = PIVOT_VALUE_NUMERIC;
1016 v->numeric.x = data->d;
1017 v->numeric.format = f;
1023 add_parents (struct pivot_category *cat, struct pivot_category *parent,
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);
1033 static const struct spvdx_facet_level *
1034 find_facet_level (const struct spvdx_visualization *v, int facet_level)
1036 const struct spvdx_facet_layout *layout = v->graph->facet_layout;
1037 for (size_t i = 0; i < layout->n_facet_level; i++)
1039 const struct spvdx_facet_level *fl = layout->facet_level[i];
1040 if (facet_level == fl->level)
1047 should_show_label (const struct spvdx_facet_level *fl)
1049 return fl && fl->axis->label && fl->axis->label->style->visible != 0;
1053 max_category (const struct spv_series *s)
1055 double max_cat = -DBL_MAX;
1056 for (size_t i = 0; i < s->n_values; i++)
1058 const struct spv_data_value *dv = &s->values[i];
1059 double d = dv->width < 0 ? dv->d : dv->index;
1063 assert (max_cat >= 0 && max_cat < SIZE_MAX - 1);
1069 add_affixes (struct pivot_table *table, struct pivot_value *value,
1070 struct spvdx_affix **affixes, size_t n_affixes)
1072 for (size_t i = 0; i < n_affixes; i++)
1073 add_footnote (value, affixes[i]->defines_reference, table);
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)
1083 const struct spvdx_facet_level *fl
1084 = find_facet_level (v, base_facet_level + n);
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]
1092 if (area && fl->axis->label)
1094 area_style_uninit (area);
1095 decode_spvdx_style (fl->axis->label->style,
1096 fl->axis->label->text_frame_style, area);
1100 if (axis_type == PIVOT_AXIS_ROW)
1102 const struct spvdx_facet_level *fl2
1103 = find_facet_level (v, base_facet_level + (n - 1));
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]);
1111 const struct spvdx_facet_level *fl3 = find_facet_level (v, base_facet_level);
1112 if (fl3 && fl3->axis->major_ticks->label_angle == -90)
1114 if (axis_type == PIVOT_AXIS_COLUMN)
1115 table->rotate_inner_column_labels = true;
1117 table->rotate_outer_row_labels = true;
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++)
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)
1132 if (cat_rows[row] == SIZE_MAX)
1137 /* Drop missing categories and count what's left. */
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);
1144 /* Make the categories. */
1145 struct pivot_dimension *d = xzalloc (sizeof *d);
1146 table->dimensions[table->n_dimensions++] = d;
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++)
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);
1161 cat->data_index = k;
1162 cat->presentation_index = cat_rows[k];
1164 series[0]->index_to_category[dv_num] = cat;
1166 add_affixes (table, cat->name, series[0]->affixes, series[0]->n_affixes);
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),
1179 .show_label = should_show_label (fl),
1180 .data_index = SIZE_MAX,
1181 .presentation_index = SIZE_MAX,
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;
1187 /* Now group them, in one pass per grouping variable, innermost first. */
1188 for (size_t j = 1; j < n; j++)
1190 struct pivot_category **new_cats = xnmalloc (n_cats, sizeof **cats);
1191 size_t n_new_cats = 0;
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; )
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];
1206 for (cat2 = cat1 + 1; cat2 < n_cats; cat2++)
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))
1213 size_t n_subs = cat2 - cat1;
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)
1220 /* The existing category stands on its own. */
1221 new_cat = cats[cat1++];
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),
1230 .subs = xnmalloc (n_subs, sizeof *new_cat->subs),
1233 .data_index = SIZE_MAX,
1234 .presentation_index = row1,
1236 for (size_t k = 0; k < n_subs; k++)
1237 new_cat->subs[k] = cats[cat1++];
1239 int dv1_num = dv1->width < 0 ? dv1->d : dv1->index;
1240 series[j]->index_to_category[dv1_num] = new_cat;
1243 add_affixes (table, new_cat->name,
1244 series[j]->affixes, series[j]->n_affixes);
1246 /* Append the new group to the list of new groups. */
1247 new_cats[n_new_cats++] = new_cat;
1252 n_cats = n_new_cats;
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);
1259 d->root->subs = cats;
1260 d->root->n_subs = n_cats;
1262 dim_seriesp[(*n_dim_seriesp)++] = series[0];
1263 series[0]->dimension = d;
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;
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,
1280 struct pivot_axis *axis = &table->axes[axis_type];
1287 struct spv_series **series = xnmalloc (nest->n_vars, sizeof *series);
1288 for (size_t i = 0; i < nest->n_vars; )
1291 for (n = 0; i + n < nest->n_vars; n++)
1293 series[n] = spv_series_from_ref (series_map, nest->vars[i + n]->ref);
1294 if (!series[n] || !series[n]->n_values)
1299 add_dimension (series, n, axis_type, v, table,
1300 dim_seriesp, n_dim_seriesp, level_ofs + i);
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,
1313 struct pivot_axis *axis = &table->axes[PIVOT_AXIS_LAYER];
1320 struct spv_series **series = xnmalloc (n_layers, sizeof *series);
1321 for (size_t i = 0; i < n_layers; )
1324 for (n = 0; i + n < n_layers; n++)
1326 series[n] = spv_series_from_ref (series_map,
1327 layers[i + n]->variable);
1328 if (!series[n] || !series[n]->n_values)
1334 struct pivot_dimension *d = add_dimension (
1335 series, n, PIVOT_AXIS_LAYER, v, table,
1336 dim_seriesp, n_dim_seriesp, level_ofs + i);
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;
1351 optional_int (int x, int default_value)
1353 return x != INT_MIN ? x : default_value;
1356 static enum pivot_area
1357 pivot_area_from_name (const char *name)
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",
1370 enum pivot_area area;
1371 for (area = 0; area < PIVOT_N_AREAS; area++)
1372 if (!strcmp (name, area_names[area]))
1377 static enum pivot_border
1378 pivot_border_from_name (const char *name)
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",
1402 enum pivot_border border;
1403 for (border = 0; border < PIVOT_N_BORDERS; border++)
1404 if (!strcmp (name, border_names[border]))
1409 static struct pivot_category *
1410 find_category (struct spv_series *series, int index)
1412 return (index >= 0 && index < series->n_index
1413 ? series->index_to_category[index]
1418 int_in_array (int value, const int *array, size_t n)
1420 for (size_t i = 0; i < n; i++)
1421 if (array[i] == value)
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)
1439 free (value->footnotes);
1440 value->footnotes = NULL;
1441 value->n_footnotes = 0;
1444 struct fmt_spec format = { .w = 0 };
1447 format = decode_format (sf->format);
1448 add_affixes (table, value, sf->format->affix, sf->format->n_affix);
1450 else if (sf->number_format)
1452 format = decode_number_format (sf->number_format);
1453 add_affixes (table, value, sf->number_format->affix,
1454 sf->number_format->n_affix);
1456 else if (sf->n_string_format)
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);
1462 else if (sf->date_time_format)
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);
1468 else if (sf->elapsed_time_format)
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);
1477 if (value->type == PIVOT_VALUE_NUMERIC)
1478 value->numeric.format = format;
1480 /* Possibly we should try to apply date and time formats too,
1481 but none seem to occur in practice so far. */
1486 struct area_style area;
1487 pivot_value_get_style (
1489 value->font_style ? value->font_style : &base_area_style->font_style,
1490 value->cell_style ? value->cell_style : &base_area_style->cell_style,
1492 decode_spvdx_style_incremental (fg, bg, &area);
1493 pivot_value_set_style (value, &area);
1494 area_style_uninit (&area);
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)
1509 if (graph && labeling && intersect->alternating
1510 && !interval && !major_ticks && !frame && !set_format)
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);
1522 && !labeling && !interval && !major_ticks && !frame && !set_format)
1524 /* 'graph->width' likely just sets the width of the table as a
1527 else if (!graph && !labeling && !interval && !frame && !set_format
1530 /* No-op. (Presumably there's a setMetaData we don't care about.) */
1532 else if (((set_format && spvdx_is_major_ticks (set_format->target))
1533 || major_ticks || frame)
1534 && intersect->n_where == 1)
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);
1541 const char *p = w->include;
1546 int include = strtol (p, &tail, 10);
1548 struct pivot_category *c = find_category (s, include);
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);
1566 else if ((set_format && spvdx_is_labeling (set_format->target))
1567 || labeling || interval)
1569 /* Formatting for individual cells or groups of them with some dimensions
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);
1575 for (size_t i = 0; i < intersect->n_where; i++)
1577 const struct spvdx_where *w = intersect->where[i];
1578 struct spv_series *s = spv_series_find (series_map, w->variable->id);
1582 /* Group indexes may be included even though they are redundant.
1587 size_t j = s->dimension->top_index;
1589 const char *p = w->include;
1593 int include = strtol (p, &tail, 10);
1595 struct pivot_category *c = find_category (s, include);
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;
1614 for (size_t i = 0; i < table->n_dimensions; i++)
1618 printf (" %d=(", i);
1619 for (size_t j = 0; j < n[i]; j++)
1623 printf ("%d", indexes[i][j]);
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
1638 struct pivot_cell *cell;
1639 HMAP_FOR_EACH (cell, struct pivot_cell, hmap_node, &table->cells)
1641 for (size_t i = 0; i < table->n_dimensions; i++)
1643 if (n[i] && !int_in_array (cell->idx[i], indexes[i], n[i]))
1646 apply_styles_to_value (table, cell->value, set_format,
1647 &table->areas[PIVOT_AREA_DATA],
1648 labeling, interval);
1653 for (size_t i = 0; i < table->n_dimensions; i++)
1664 decode_set_cell_properties (struct pivot_table *table, struct hmap *series_map,
1665 struct spvdx_set_cell_properties **scps,
1668 for (size_t i = 0; i < n_scps; i++)
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++)
1679 const struct spvxml_node *node = scp->seq[j];
1680 if (spvdx_is_set_style (node))
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;
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);
1700 assert (spvdx_is_set_meta_data (node));
1703 if (scp->union_ && scp->apply_to_converse <= 0)
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);
1710 else if (!scp->union_ && scp->apply_to_converse > 0)
1712 if ((set_format && spvdx_is_labeling (set_format->target))
1713 || labeling || interval)
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],
1722 else if (!scp->union_ && scp->apply_to_converse <= 0)
1724 /* Appears to be used to set the font for something--but what? */
1731 char * WARN_UNUSED_RESULT
1732 decode_spvsx_legacy_properties (const struct spvsx_table_properties *in,
1733 struct spv_legacy_properties **outp)
1735 struct spv_legacy_properties *out = xzalloc (sizeof *out);
1740 error = xstrdup ("Legacy table lacks tableProperties");
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;
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);
1759 for (int i = 0; i < PIVOT_N_AREAS; i++)
1760 pivot_area_get_default_style (i, &out->areas[i]);
1762 const struct spvsx_cell_format_properties *cfp = in->cell_format_properties;
1763 for (size_t i = 0; i < cfp->n_cell_style; i++)
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)
1770 error = xasprintf ("unknown area \"%s\" in cellFormatProperties",
1775 struct area_style *a = &out->areas[area];
1776 const struct spvsx_style *s = c->style;
1778 a->font_style.bold = s->font_weight == SPVSX_FONT_WEIGHT_BOLD;
1780 a->font_style.italic = s->font_style == SPVSX_FONT_STYLE_ITALIC;
1781 a->font_style.underline = false;
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]);
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]);
1796 free (a->font_style.typeface);
1797 a->font_style.typeface = xstrdup (s->font_family);
1801 a->font_style.size = optional_length (s->font_size, 0);
1803 if (s->text_alignment)
1804 a->cell_style.halign
1805 = (s->text_alignment == SPVSX_TEXT_ALIGNMENT_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
1820 : TABLE_VALIGN_CENTER);
1822 if (s->decimal_offset != DBL_MAX)
1823 a->cell_style.decimal_offset = optional_px (s->decimal_offset, 0);
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,
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,
1837 for (int i = 0; i < PIVOT_N_BORDERS; i++)
1838 pivot_border_get_default_style (i, &out->borders[i]);
1840 const struct spvsx_border_properties *bp = in->border_properties;
1841 for (size_t i = 0; i < bp->n_border_style; i++)
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)
1848 error = xasprintf ("unknown border \"%s\" parsing borderProperties",
1853 struct table_border_style *bout = &out->borders[border];
1855 = (bin->border_style_type == SPVSX_BORDER_STYLE_TYPE_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
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);
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);
1885 spv_legacy_properties_destroy (out);
1891 spv_legacy_properties_destroy (struct spv_legacy_properties *props)
1895 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
1896 area_style_uninit (&props->areas[i]);
1897 free (props->continuation);
1902 static struct spv_series *
1903 parse_formatting (const struct spvdx_visualization *v,
1904 const struct hmap *series_map, struct hmap *format_map)
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++)
1910 const struct spvdx_formatting *f
1911 = spvdx_cast_formatting (labeling->seq[i]);
1915 cell_format = spv_series_from_ref (series_map, f->variable);
1916 for (size_t j = 0; j < f->n_format_mapping; j++)
1918 const struct spvdx_format_mapping *fm = f->format_mapping[j];
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));
1935 format_map_destroy (struct hmap *format_map)
1937 struct format_mapping *fm, *next;
1938 HMAP_FOR_EACH_SAFE (fm, next, struct format_mapping, hmap_node, format_map)
1940 hmap_delete (format_map, &fm->hmap_node);
1943 hmap_destroy (format_map);
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)
1951 struct pivot_table *table = pivot_table_create__ (NULL);
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;
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;
1966 table->footnote_marker_superscripts = props->footnote_marker_superscripts;
1967 table->show_numeric_markers = props->show_numeric_markers;
1969 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
1971 area_style_uninit (&table->areas[i]);
1972 area_style_copy (&table->areas[i], &props->areas[i]);
1974 for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
1975 table->borders[i] = props->borders[i];
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;
1986 struct spvdx_visualization_extension *ve = v->visualization_extension;
1987 table->show_grid_lines = ve && ve->show_gridline;
1989 /* Sizing from the legacy properties can get overridden. */
1990 if (v->graph->cell_style->width)
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')
1997 table->sizing[TABLE_HORZ].range[0] = min_width;
1998 table->sizing[TABLE_HORZ].range[1] = max_width;
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.
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. */
2015 for (size_t i = 0; i < v->container->n_label_frame; i++)
2017 const struct spvdx_label_frame *lf = v->container->label_frame[i];
2019 && lf->label->purpose == SPVDX_PURPOSE_FOOTNOTE
2020 && lf->label->n_text > 0
2021 && lf->label->text[0]->uses_reference > 0)
2023 pivot_table_create_footnote__ (
2024 table, lf->label->text[0]->uses_reference - 1,
2029 if (v->graph->interval->footnotes)
2030 decode_footnotes (table, v->graph->interval->footnotes);
2032 struct spv_series *footnotes = NULL;
2033 for (size_t i = 0; i < v->graph->interval->labeling->n_seq; i++)
2035 const struct spvxml_node *node = v->graph->interval->labeling->seq[i];
2036 if (spvdx_is_footnotes (node))
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);
2043 for (size_t i = 0; i < v->n_lf1; i++)
2045 error = decode_label_frame (table, v->lf1[i]);
2049 for (size_t i = 0; i < v->n_lf2; i++)
2051 error = decode_label_frame (table, v->lf2[i]);
2056 for (size_t i = 0; i < v->container->n_label_frame; i++)
2058 error = decode_label_frame (table, v->container->label_frame[i]);
2062 if (v->graph->interval->labeling->style)
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]);
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;
2075 bool progress = false;
2076 for (size_t i = 0; i < n_nodes; )
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));
2083 nodes[i] = nodes[--n_nodes];
2086 else if (error == &BAD_REFERENCE)
2098 error = xasprintf ("Table has %zu variables with circular or "
2099 "unresolved references, including variable %s.",
2100 n_nodes, nodes[0]->id);
2106 const struct spvdx_cross *cross = v->graph->faceting->cross;
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;
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;
2116 size_t max_layers = (v->graph->faceting->n_layers1
2117 + v->graph->faceting->n_layers2);
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);
2135 struct spv_series *cell = spv_series_find (&series_map, "cell");
2138 error = xstrdup (_("Table lacks cell data."));
2142 struct spv_series *cell_format = parse_formatting (v, &series_map,
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++)
2149 for (size_t j = 0; j < table->n_dimensions; j++)
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);
2156 dim_indexes[j] = cat->data_index;
2159 struct pivot_value *value = pivot_value_from_data_value (
2160 &cell->values[i], cell_format ? &cell_format->values[i] : NULL,
2164 const struct spv_data_value *d = &footnotes->values[i];
2167 const char *p = d->s;
2171 int idx = strtol (p, &tail, 10);
2172 add_footnote (value, idx, table);
2182 if (value->type == PIVOT_VALUE_NUMERIC
2183 && value->numeric.x == SYSMIS
2184 && !value->n_footnotes)
2186 /* Apparently, system-missing values are just empty cells? */
2187 pivot_value_destroy (value);
2190 pivot_table_put (table, dim_indexes, table->n_dimensions, value);
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);
2200 pivot_table_assign_label_depth (table);
2202 format_map_destroy (&format_map);
2206 spv_series_destroy (&series_map);
2209 pivot_table_unref (table);