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 return spv_series_find (series_map, node->id);
136 spv_series_dump (const struct spv_series *series)
138 printf ("series \"%s\"", series->name);
140 printf (" (label \"%s\")", series->label);
141 printf (", %zu values:", series->n_values);
142 for (size_t i = 0; i < series->n_values; i++)
145 spv_data_value_dump (&series->values[i], stdout);
151 spv_series_destroy (struct hmap *series_map)
153 struct spv_series *series, *next_series;
154 HMAP_FOR_EACH_SAFE (series, next_series, struct spv_series, hmap_node,
158 free (series->label);
160 for (size_t i = 0; i < series->n_values; i++)
161 spv_data_value_uninit (&series->values[i]);
162 free (series->values);
164 spv_map_destroy (&series->map);
166 free (series->index_to_category);
168 hmap_delete (series_map, &series->hmap_node);
171 hmap_destroy (series_map);
176 struct hmap_node hmap_node;
178 struct spv_data_value to;
181 static struct spv_mapping *
182 spv_map_search (const struct hmap *map, double from)
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)
192 static const struct spv_data_value *
193 spv_map_lookup (const struct hmap *map, const struct spv_data_value *in)
198 const struct spv_mapping *m = spv_map_search (map, in->d);
199 return m ? &m->to : in;
203 parse_real (const char *s, double *real)
205 int save_errno = errno;
208 *real = c_strtod (s, &end);
209 bool ok = !errno && end > s && !*end;
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)
219 struct spv_mapping *mapping = xmalloc (sizeof *mapping);
220 mapping->from = from;
222 if ((try_strings_as_numbers || (format && fmt_is_numeric (format->type)))
223 && parse_real (to, &mapping->to.d))
225 if (try_strings_as_numbers)
226 mapping->to.width = -1;
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);
236 mapping->to.width = strlen (to);
237 mapping->to.s = xstrdup (to);
240 struct spv_mapping *old_mapping = spv_map_search (map, from);
243 bool same = spv_data_value_equal (&old_mapping->to, &mapping->to);
244 spv_data_value_uninit (&mapping->to);
247 : xasprintf ("Duplicate relabeling differs for from=\"%.*g\"",
251 hmap_insert (map, &mapping->hmap_node, hash_double (from, 0));
256 spv_map_destroy (struct hmap *map)
258 struct spv_mapping *mapping, *next;
259 HMAP_FOR_EACH_SAFE (mapping, next, struct spv_mapping, hmap_node, map)
261 spv_data_value_uninit (&mapping->to);
262 hmap_delete (map, &mapping->hmap_node);
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)
274 for (size_t i = 0; i < n_relabels; i++)
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);
285 static char * WARN_UNUSED_RESULT
286 spv_series_parse_value_map_entry (struct hmap *map,
287 const struct spvdx_value_map_entry *vme)
289 for (const char *p = vme->from; ; p++)
291 int save_errno = errno;
294 double from = c_strtod (p, &end);
295 bool ok = !errno && end > p && strchr (";", *end);
298 return xasprintf ("Syntax error in valueMapEntry from=\"%s\".",
301 char *error = spv_map_insert (map, from, vme->to, true,
302 &(struct fmt_spec) { FMT_A, 40, 0 });
313 static struct fmt_spec
314 decode_date_time_format (const struct spvdx_date_time_format *dtf)
316 if (dtf->dt_base_format == SPVDX_DT_BASE_FORMAT_DATE)
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
328 int w = fmt_min_output_width (type);
329 if (dtf->year_abbreviation <= 0)
331 return (struct fmt_spec) { .type = type, .w = w };
336 = (dtf->dt_base_format == SPVDX_DT_BASE_FORMAT_DATE_TIME
337 ? (dtf->mdy_order == SPVDX_MDY_ORDER_YEAR_MONTH_DAY
340 : (dtf->show_day > 0 ? FMT_DTIME
341 : dtf->show_hour > 0 ? FMT_TIME
343 int w = fmt_min_output_width (type);
345 if (dtf->show_second > 0)
348 if (dtf->show_millis > 0)
354 return (struct fmt_spec) { .type = type, .w = w, .d = d };
358 static struct fmt_spec
359 decode_elapsed_time_format (const struct spvdx_elapsed_time_format *etf)
362 = (etf->dt_base_format != SPVDX_DT_BASE_FORMAT_TIME ? FMT_DTIME
363 : etf->show_hour > 0 ? FMT_TIME
365 int w = fmt_min_output_width (type);
367 if (etf->show_second > 0)
370 if (etf->show_millis > 0)
376 return (struct fmt_spec) { .type = type, .w = w, .d = d };
379 static struct fmt_spec
380 decode_number_format (const struct spvdx_number_format *nf)
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
388 int d = nf->maximum_fraction_digits;
392 struct fmt_spec f = (struct fmt_spec) { type, 40, d };
397 /* Returns an *approximation* of IN as a fmt_spec.
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)
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)
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,
440 return decode_date_time_format (&dtf);
442 else if (in->f_base_format == SPVDX_F_BASE_FORMAT_ELAPSED_TIME)
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,
460 return decode_elapsed_time_format (&etf);
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,
472 .prefix = in->prefix,
473 .suffix = in->suffix,
475 return decode_number_format (&nf);
480 spv_series_execute_mapping (struct spv_series *series)
482 if (!hmap_is_empty (&series->map))
484 series->remapped = true;
485 for (size_t i = 0; i < series->n_values; i++)
487 struct spv_data_value *value = &series->values[i];
488 if (value->width >= 0)
491 const struct spv_mapping *mapping = spv_map_search (&series->map,
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);
501 value->d = mapping->to.d;
507 static char * WARN_UNUSED_RESULT
508 spv_series_remap_formats (struct spv_series *series,
509 struct spvxml_node **seq, size_t n_seq)
511 spv_map_destroy (&series->map);
512 hmap_init (&series->map);
513 for (size_t i = 0; i < n_seq; i++)
515 struct spvxml_node *node = seq[i];
516 if (spvdx_is_format (node))
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);
526 series->affixes = f->affix;
527 series->n_affixes = f->n_affix;
529 else if (spvdx_is_string_format (node))
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,
538 series->affixes = sf->affix;
539 series->n_affixes = sf->n_affix;
544 spv_series_execute_mapping (series);
548 static char * WARN_UNUSED_RESULT
549 spv_series_remap_vmes (struct spv_series *series,
550 struct spvdx_value_map_entry **vmes,
553 spv_map_destroy (&series->map);
554 hmap_init (&series->map);
555 for (size_t i = 0; i < n_vmes; i++)
557 char *error = spv_series_parse_value_map_entry (&series->map, vmes[i]);
561 spv_series_execute_mapping (series);
566 decode_footnotes (struct pivot_table *table, const struct spvdx_footnotes *f)
568 if (f->n_footnote_mapping > 0)
569 pivot_table_create_footnote__ (table, f->n_footnote_mapping - 1,
571 for (size_t i = 0; i < f->n_footnote_mapping; i++)
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),
580 static struct cell_color
581 optional_color (int color, struct cell_color default_color)
584 ? (struct cell_color) CELL_COLOR (color >> 16, color >> 8, color)
589 optional_length (const char *s, int default_length)
591 /* There is usually a "pt" suffix. We ignore it. */
593 return s && sscanf (s, "%d", &length) == 1 ? length : default_length;
597 optional_px (double inches, int default_px)
599 return inches != DBL_MAX ? inches * 96.0 : default_px;
603 optional_pt (double inches, int default_pt)
605 return inches != DBL_MAX ? inches * 72.0 + .5 : default_pt;
609 decode_spvdx_style_incremental (const struct spvdx_style *in,
610 const struct spvdx_style *bg,
611 struct area_style *out)
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)
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];
625 if (bg && bg->color >= 0)
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];
631 if (in && in->font_family)
633 free (out->font_style.typeface);
634 out->font_style.typeface = xstrdup (in->font_family);
636 if (in && in->font_size)
638 int size = optional_length (in->font_size, 0);
640 out->font_style.size = size;
642 if (in && in->text_alignment)
643 out->cell_style.halign
644 = (in->text_alignment == SPVDX_TEXT_ALIGNMENT_LEFT
646 : in->text_alignment == SPVDX_TEXT_ALIGNMENT_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
659 : TABLE_VALIGN_CENTER);
660 if (in && in->decimal_offset != DBL_MAX)
661 out->cell_style.decimal_offset = optional_px (in->decimal_offset, 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);
675 decode_spvdx_style (const struct spvdx_style *in,
676 const struct spvdx_style *bg,
677 struct area_style *out)
679 *out = (struct area_style) AREA_STYLE_INITIALIZER;
680 decode_spvdx_style_incremental (in, bg, out);
684 add_footnote (struct pivot_value *v, int idx, struct pivot_table *table)
686 if (idx < 1 || idx > table->n_footnotes)
689 pivot_value_add_footnote (v, table->footnotes[idx - 1]);
692 static char * WARN_UNUSED_RESULT
693 decode_label_frame (struct pivot_table *table,
694 const struct spvdx_label_frame *lf)
699 struct pivot_value **target;
700 struct area_style *area;
701 if (lf->label->purpose == SPVDX_PURPOSE_TITLE)
703 target = &table->title;
704 area = &table->areas[PIVOT_AREA_TITLE];
706 else if (lf->label->purpose == SPVDX_PURPOSE_SUB_TITLE)
708 target = &table->caption;
709 area = &table->areas[PIVOT_AREA_CAPTION];
711 else if (lf->label->purpose == SPVDX_PURPOSE_FOOTNOTE)
713 if (lf->label->n_text > 0
714 && lf->label->text[0]->uses_reference != INT_MIN)
717 area = &table->areas[PIVOT_AREA_FOOTER];
722 else if (lf->label->purpose == SPVDX_PURPOSE_LAYER)
725 area = &table->areas[PIVOT_AREA_LAYERS];
730 area_style_uninit (area);
731 decode_spvdx_style (lf->label->style, lf->label->text_frame_style, area);
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++)
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);
746 char *new = xasprintf ("%s%s", value->text.local, in->text);
747 free (value->text.local);
748 value->text.local = new;
751 pivot_value_destroy (*target);
755 for (size_t i = 0; i < lf->label->n_text; i++)
757 const struct spvdx_text *in = lf->label->text[i];
758 if (in->uses_reference == INT_MIN)
762 size_t length = strlen (in->text);
763 if (length && in->text[length - 1] == '\n')
766 pivot_table_create_footnote__ (
767 table, in->uses_reference - 1, NULL,
768 pivot_value_new_user_text (in->text, length));
772 size_t length = strlen (in->text);
773 if (length && in->text[length - 1] == '.')
776 pivot_table_create_footnote__ (
777 table, in->uses_reference - 1,
778 pivot_value_new_user_text (in->text, length), NULL);
784 /* Special return value for decode_spvdx_variable(). */
785 static char BAD_REFERENCE;
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)
792 const struct spvdx_source_variable *sv = spvdx_cast_source_variable (node);
794 struct spv_series *label_series = NULL;
795 if (sv->label_variable)
797 label_series = spv_series_find (series_map,
798 sv->label_variable->node_.id);
800 return &BAD_REFERENCE;
802 label_series->is_label_series = true;
805 const struct spv_data_variable *var = spv_data_find_variable (
806 data, sv->source, sv->source_name);
808 return xasprintf ("sourceVariable %s references nonexistent "
809 "source %s variable %s.",
810 sv->node_.id, sv->source, sv->source_name);
812 struct spv_series *s = xzalloc (sizeof *s);
813 s->name = xstrdup (node->id);
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;
821 hmap_insert (series_map, &s->hmap_node, hash_string (s->name, 0));
823 char *error = spv_series_remap_formats (s, sv->seq, sv->n_seq);
827 if (label_series && !s->remapped)
829 for (size_t i = 0; i < s->n_values; i++)
830 if (s->values[i].width < 0)
833 if (label_series->values[i].width < 0)
835 union value v = { .f = label_series->values[i].d };
836 dest = data_out_stretchy (&v, "UTF-8", &s->format, NULL);
839 dest = label_series->values[i].s;
840 char *error = spv_map_insert (&s->map, s->values[i].d,
842 free (error); /* Duplicates are OK. */
843 if (label_series->values[i].width < 0)
851 static char * WARN_UNUSED_RESULT
852 decode_spvdx_derived_variable (const struct spvxml_node *node,
853 struct hmap *series_map)
855 const struct spvdx_derived_variable *dv = spvdx_cast_derived_variable (node);
857 struct spv_data_value *values;
860 struct substring value = ss_cstr (dv->value);
861 if (ss_equals (value, ss_cstr ("constant(0)")))
863 struct spv_series *existing_series = spv_series_first (series_map);
864 if (!existing_series)
865 return &BAD_REFERENCE;
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;
872 else if (ss_starts_with (value, ss_cstr ("constant(")))
877 else if (ss_starts_with (value, ss_cstr ("map("))
878 && ss_ends_with (value, ss_cstr (")")))
880 char *dependency_name = ss_xstrdup (ss_substr (value, 4,
882 struct spv_series *dependency
883 = spv_series_find (series_map, dependency_name);
884 free (dependency_name);
886 return &BAD_REFERENCE;
888 values = spv_data_values_clone (dependency->values,
889 dependency->n_values);
890 n_values = dependency->n_values;
893 return xasprintf ("Derived variable %s has unknown value \"%s\"",
894 node->id, dv->value);
896 struct spv_series *s = xzalloc (sizeof *s);
898 s->name = xstrdup (node->id);
900 s->n_values = n_values;
902 hmap_insert (series_map, &s->hmap_node, hash_string (s->name, 0));
904 char *error = spv_series_remap_vmes (s, dv->value_map_entry,
905 dv->n_value_map_entry);
909 error = spv_series_remap_formats (s, dv->seq, dv->n_seq);
915 for (size_t i = 0; i < n_values; i++)
916 if (values[i].width != 0)
918 for (size_t i = 0; i < n_values; i++)
919 spv_data_value_uninit (&s->values[i]);
930 struct format_mapping
932 struct hmap_node hmap_node;
937 static const struct format_mapping *
938 format_map_find (const struct hmap *format_map, uint32_t u32_format)
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)
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)
959 *out = fmt_for_output (FMT_F, 40, 2);
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);
970 return spv_decode_fmt_spec (u32_format, out);
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)
982 char *error = spv_format_from_data_value (format, format_map, &f);
986 struct pivot_value *v = xzalloc (sizeof *v);
987 if (data->width >= 0)
989 if (format && fmt_get_category (f.type) == FMT_CAT_DATE)
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,
996 && data->s[len] == '\0')
998 double date = calendar_gregorian_to_offset (year, month, day,
1002 v->type = PIVOT_VALUE_NUMERIC;
1003 v->numeric.x = (date * 60. * 60. * 24.
1008 v->numeric.format = f;
1014 else if (format && fmt_get_category (f.type) == FMT_CAT_TIME)
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
1020 && data->s[len] == '\0')
1022 v->type = PIVOT_VALUE_NUMERIC;
1023 v->numeric.x = (hour * 60. * 60.
1027 v->numeric.format = f;
1032 v->type = PIVOT_VALUE_STRING;
1033 v->string.s = xstrdup (data->s);
1037 v->type = PIVOT_VALUE_NUMERIC;
1038 v->numeric.x = data->d;
1039 v->numeric.format = f;
1046 add_parents (struct pivot_category *cat, struct pivot_category *parent,
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);
1056 static const struct spvdx_facet_level *
1057 find_facet_level (const struct spvdx_visualization *v, int facet_level)
1059 const struct spvdx_facet_layout *layout = v->graph->facet_layout;
1060 for (size_t i = 0; i < layout->n_facet_level; i++)
1062 const struct spvdx_facet_level *fl = layout->facet_level[i];
1063 if (facet_level == fl->level)
1070 should_show_label (const struct spvdx_facet_level *fl)
1072 return fl && fl->axis->label && fl->axis->label->style->visible != 0;
1076 max_category (const struct spv_series *s)
1078 double max_cat = -DBL_MAX;
1079 for (size_t i = 0; i < s->n_values; i++)
1081 const struct spv_data_value *dv = &s->values[i];
1082 double d = dv->width < 0 ? dv->d : dv->index;
1086 assert (max_cat >= 0 && max_cat < SIZE_MAX - 1);
1092 add_affixes (struct pivot_table *table, struct pivot_value *value,
1093 struct spvdx_affix **affixes, size_t n_affixes)
1095 for (size_t i = 0; i < n_affixes; i++)
1096 add_footnote (value, affixes[i]->defines_reference, table);
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)
1108 const struct spvdx_facet_level *fl
1109 = find_facet_level (v, base_facet_level + n);
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]
1117 if (area && fl->axis->label)
1119 area_style_uninit (area);
1120 decode_spvdx_style (fl->axis->label->style,
1121 fl->axis->label->text_frame_style, area);
1125 if (axis_type == PIVOT_AXIS_ROW)
1127 const struct spvdx_facet_level *fl2
1128 = find_facet_level (v, base_facet_level + (n - 1));
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]);
1136 const struct spvdx_facet_level *fl3 = find_facet_level (v, base_facet_level);
1137 if (fl3 && fl3->axis->major_ticks->label_angle == -90)
1139 if (axis_type == PIVOT_AXIS_COLUMN)
1140 table->rotate_inner_column_labels = true;
1142 table->rotate_outer_row_labels = true;
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++)
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)
1157 if (cat_rows[row] == SIZE_MAX)
1162 /* Drop missing categories and count what's left. */
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);
1169 /* Make the categories. */
1170 struct pivot_dimension *d = xzalloc (sizeof *d);
1171 table->dimensions[table->n_dimensions++] = d;
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++)
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);
1193 cat->data_index = k;
1194 cat->presentation_index = cat_rows[k];
1196 series[0]->index_to_category[dv_num] = cat;
1199 add_affixes (table, cat->name,
1200 series[0]->affixes, series[0]->n_affixes);
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),
1213 .show_label = should_show_label (fl),
1214 .data_index = SIZE_MAX,
1215 .presentation_index = SIZE_MAX,
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;
1221 /* Now group them, in one pass per grouping variable, innermost first. */
1222 for (size_t j = 1; j < n; j++)
1224 struct pivot_category **new_cats = xnmalloc (n_cats, sizeof **cats);
1225 size_t n_new_cats = 0;
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;)
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];
1240 for (cat2 = cat1 + 1; cat2 < n_cats; cat2++)
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))
1247 size_t n_subs = cat2 - cat1;
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)
1254 /* The existing category stands on its own. */
1255 new_cat = cats[cat1++];
1259 /* Create a new group with cat...cat2 as subcategories. */
1260 new_cat = xzalloc (sizeof *new_cat);
1261 *new_cat = (struct pivot_category) {
1263 .subs = xnmalloc (n_subs, sizeof *new_cat->subs),
1266 .data_index = SIZE_MAX,
1267 .presentation_index = row1,
1269 char *retval = pivot_value_from_data_value (name, NULL, NULL,
1278 for (size_t k = 0; k < n_subs; k++)
1279 new_cat->subs[k] = cats[cat1++];
1281 int dv1_num = dv1->width < 0 ? dv1->d : dv1->index;
1282 series[j]->index_to_category[dv1_num] = new_cat;
1286 add_affixes (table, new_cat->name,
1287 series[j]->affixes, series[j]->n_affixes);
1289 /* Append the new group to the list of new groups. */
1290 new_cats[n_new_cats++] = new_cat;
1295 n_cats = n_new_cats;
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);
1302 d->root->subs = cats;
1303 d->root->n_subs = n_cats;
1307 pivot_dimension_destroy (d);
1311 dim_seriesp[(*n_dim_seriesp)++] = series[0];
1312 series[0]->dimension = d;
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;
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,
1330 struct pivot_axis *axis = &table->axes[axis_type];
1337 struct spv_series **series = xnmalloc (nest->n_vars, sizeof *series);
1338 for (size_t i = 0; i < nest->n_vars;)
1341 for (n = 0; i + n < nest->n_vars; n++)
1343 series[n] = spv_series_from_ref (series_map, nest->vars[i + n]->ref);
1344 if (!series[n] || !series[n]->n_values)
1350 struct pivot_dimension *d;
1351 char *error = add_dimension (series, n, axis_type, v, table,
1352 dim_seriesp, n_dim_seriesp,
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,
1375 struct pivot_axis *axis = &table->axes[PIVOT_AXIS_LAYER];
1382 struct spv_series **series = xnmalloc (n_layers, sizeof *series);
1383 for (size_t i = 0; i < n_layers;)
1386 for (n = 0; i + n < n_layers; n++)
1388 series[n] = spv_series_from_ref (series_map,
1389 layers[i + n]->variable);
1390 if (!series[n] || !series[n]->n_values)
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);
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;
1421 optional_int (int x, int default_value)
1423 return x != INT_MIN ? x : default_value;
1426 static enum pivot_area
1427 pivot_area_from_name (const char *name)
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",
1440 enum pivot_area area;
1441 for (area = 0; area < PIVOT_N_AREAS; area++)
1442 if (!strcmp (name, area_names[area]))
1447 static enum pivot_border
1448 pivot_border_from_name (const char *name)
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",
1472 enum pivot_border border;
1473 for (border = 0; border < PIVOT_N_BORDERS; border++)
1474 if (!strcmp (name, border_names[border]))
1479 static struct pivot_category *
1480 find_category (struct spv_series *series, int index)
1482 return (index >= 0 && index < series->n_index
1483 ? series->index_to_category[index]
1488 int_in_array (int value, const int *array, size_t n)
1490 for (size_t i = 0; i < n; i++)
1491 if (array[i] == value)
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)
1509 free (value->footnotes);
1510 value->footnotes = NULL;
1511 value->n_footnotes = 0;
1514 struct fmt_spec format = { .w = 0 };
1517 format = decode_format (sf->format);
1518 add_affixes (table, value, sf->format->affix, sf->format->n_affix);
1520 else if (sf->number_format)
1522 format = decode_number_format (sf->number_format);
1523 add_affixes (table, value, sf->number_format->affix,
1524 sf->number_format->n_affix);
1526 else if (sf->n_string_format)
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);
1532 else if (sf->date_time_format)
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);
1538 else if (sf->elapsed_time_format)
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);
1547 if (value->type == PIVOT_VALUE_NUMERIC)
1548 value->numeric.format = format;
1550 /* Possibly we should try to apply date and time formats too,
1551 but none seem to occur in practice so far. */
1556 struct area_style area;
1557 pivot_value_get_style (
1559 value->font_style ? value->font_style : &base_area_style->font_style,
1560 value->cell_style ? value->cell_style : &base_area_style->cell_style,
1562 decode_spvdx_style_incremental (fg, bg, &area);
1563 pivot_value_set_style (value, &area);
1564 area_style_uninit (&area);
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)
1579 if (graph && labeling && intersect->alternating
1580 && !interval && !major_ticks && !frame && !set_format)
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);
1592 && !labeling && !interval && !major_ticks && !frame && !set_format)
1594 /* 'graph->width' likely just sets the width of the table as a
1597 else if (!graph && !labeling && !interval && !frame && !set_format
1600 /* No-op. (Presumably there's a setMetaData we don't care about.) */
1602 else if (((set_format && spvdx_is_major_ticks (set_format->target))
1603 || major_ticks || frame)
1604 && intersect->n_where == 1)
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);
1611 const char *p = w->include;
1616 int include = strtol (p, &tail, 10);
1618 struct pivot_category *c = find_category (s, include);
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);
1636 else if ((set_format && spvdx_is_labeling (set_format->target))
1637 || labeling || interval)
1639 /* Formatting for individual cells or groups of them with some dimensions
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);
1645 for (size_t i = 0; i < intersect->n_where; i++)
1647 const struct spvdx_where *w = intersect->where[i];
1648 struct spv_series *s = spv_series_find (series_map, w->variable->id);
1652 /* Group indexes may be included even though they are redundant.
1657 size_t j = s->dimension->top_index;
1659 const char *p = w->include;
1663 int include = strtol (p, &tail, 10);
1665 struct pivot_category *c = find_category (s, include);
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;
1684 for (size_t i = 0; i < table->n_dimensions; i++)
1688 printf (" %d=(", i);
1689 for (size_t j = 0; j < n[i]; j++)
1693 printf ("%d", indexes[i][j]);
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
1708 struct pivot_cell *cell;
1709 HMAP_FOR_EACH (cell, struct pivot_cell, hmap_node, &table->cells)
1711 for (size_t i = 0; i < table->n_dimensions; i++)
1713 if (n[i] && !int_in_array (cell->idx[i], indexes[i], n[i]))
1716 apply_styles_to_value (table, cell->value, set_format,
1717 &table->areas[PIVOT_AREA_DATA],
1718 labeling, interval);
1723 for (size_t i = 0; i < table->n_dimensions; i++)
1734 decode_set_cell_properties (struct pivot_table *table, struct hmap *series_map,
1735 struct spvdx_set_cell_properties **scps,
1738 for (size_t i = 0; i < n_scps; i++)
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++)
1749 const struct spvxml_node *node = scp->seq[j];
1750 if (spvdx_is_set_style (node))
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;
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);
1770 assert (spvdx_is_set_meta_data (node));
1773 if (scp->union_ && scp->apply_to_converse <= 0)
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);
1780 else if (!scp->union_ && scp->apply_to_converse > 0)
1782 if ((set_format && spvdx_is_labeling (set_format->target))
1783 || labeling || interval)
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],
1792 else if (!scp->union_ && scp->apply_to_converse <= 0)
1794 /* Appears to be used to set the font for something--but what? */
1801 char * WARN_UNUSED_RESULT
1802 decode_spvsx_legacy_properties (const struct spvsx_table_properties *in,
1803 struct spv_legacy_properties **outp)
1805 struct spv_legacy_properties *out = xzalloc (sizeof *out);
1810 error = xstrdup ("Legacy table lacks tableProperties");
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;
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);
1829 for (int i = 0; i < PIVOT_N_AREAS; i++)
1830 area_style_copy (NULL, &out->areas[i], pivot_area_get_default_style (i));
1832 const struct spvsx_cell_format_properties *cfp = in->cell_format_properties;
1833 for (size_t i = 0; i < cfp->n_cell_style; i++)
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)
1840 error = xasprintf ("unknown area \"%s\" in cellFormatProperties",
1845 struct area_style *a = &out->areas[area];
1846 const struct spvsx_style *s = c->style;
1848 a->font_style.bold = s->font_weight == SPVSX_FONT_WEIGHT_BOLD;
1850 a->font_style.italic = s->font_style == SPVSX_FONT_STYLE_ITALIC;
1851 a->font_style.underline = false;
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]);
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]);
1866 free (a->font_style.typeface);
1867 a->font_style.typeface = xstrdup (s->font_family);
1871 a->font_style.size = optional_length (s->font_size, 0);
1873 if (s->text_alignment)
1874 a->cell_style.halign
1875 = (s->text_alignment == SPVSX_TEXT_ALIGNMENT_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
1890 : TABLE_VALIGN_CENTER);
1892 if (s->decimal_offset != DBL_MAX)
1893 a->cell_style.decimal_offset = optional_px (s->decimal_offset, 0);
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,
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,
1907 for (int i = 0; i < PIVOT_N_BORDERS; i++)
1908 pivot_border_get_default_style (i, &out->borders[i]);
1910 const struct spvsx_border_properties *bp = in->border_properties;
1911 for (size_t i = 0; i < bp->n_border_style; i++)
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)
1918 error = xasprintf ("unknown border \"%s\" parsing borderProperties",
1923 struct table_border_style *bout = &out->borders[border];
1925 = (bin->border_style_type == SPVSX_BORDER_STYLE_TYPE_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
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);
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);
1955 spv_legacy_properties_destroy (out);
1961 spv_legacy_properties_destroy (struct spv_legacy_properties *props)
1965 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
1966 area_style_uninit (&props->areas[i]);
1967 free (props->continuation);
1972 static struct spv_series *
1973 parse_formatting (const struct spvdx_visualization *v,
1974 const struct hmap *series_map, struct hmap *format_map)
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++)
1980 const struct spvdx_formatting *f
1981 = spvdx_cast_formatting (labeling->seq[i]);
1985 cell_format = spv_series_from_ref (series_map, f->variable);
1986 for (size_t j = 0; j < f->n_format_mapping; j++)
1988 const struct spvdx_format_mapping *fm = f->format_mapping[j];
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));
2005 format_map_destroy (struct hmap *format_map)
2007 struct format_mapping *fm, *next;
2008 HMAP_FOR_EACH_SAFE (fm, next, struct format_mapping, hmap_node, format_map)
2010 hmap_delete (format_map, &fm->hmap_node);
2013 hmap_destroy (format_map);
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)
2021 struct pivot_table *table = pivot_table_create__ (NULL, subtype);
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;
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;
2036 table->footnote_marker_superscripts = props->footnote_marker_superscripts;
2037 table->show_numeric_markers = props->show_numeric_markers;
2039 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
2041 area_style_uninit (&table->areas[i]);
2042 area_style_copy (NULL, &table->areas[i], &props->areas[i]);
2044 for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
2045 table->borders[i] = props->borders[i];
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;
2056 struct spvdx_visualization_extension *ve = v->visualization_extension;
2057 table->show_grid_lines = ve && ve->show_gridline;
2059 /* Sizing from the legacy properties can get overridden. */
2060 if (v->graph->cell_style->width)
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')
2067 table->sizing[TABLE_HORZ].range[0] = min_width;
2068 table->sizing[TABLE_HORZ].range[1] = max_width;
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.
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. */
2085 for (size_t i = 0; i < v->container->n_label_frame; i++)
2087 const struct spvdx_label_frame *lf = v->container->label_frame[i];
2089 && lf->label->purpose == SPVDX_PURPOSE_FOOTNOTE
2090 && lf->label->n_text > 0
2091 && lf->label->text[0]->uses_reference > 0)
2093 pivot_table_create_footnote__ (
2094 table, lf->label->text[0]->uses_reference - 1,
2099 if (v->graph->interval->footnotes)
2100 decode_footnotes (table, v->graph->interval->footnotes);
2102 struct spv_series *footnotes = NULL;
2103 for (size_t i = 0; i < v->graph->interval->labeling->n_seq; i++)
2105 const struct spvxml_node *node = v->graph->interval->labeling->seq[i];
2106 if (spvdx_is_footnotes (node))
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);
2113 for (size_t i = 0; i < v->n_lf1; i++)
2115 error = decode_label_frame (table, v->lf1[i]);
2119 for (size_t i = 0; i < v->n_lf2; i++)
2121 error = decode_label_frame (table, v->lf2[i]);
2126 for (size_t i = 0; i < v->container->n_label_frame; i++)
2128 error = decode_label_frame (table, v->container->label_frame[i]);
2132 if (v->graph->interval->labeling->style)
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]);
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;
2145 bool progress = false;
2146 for (size_t i = 0; i < n_nodes;)
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));
2153 nodes[i] = nodes[--n_nodes];
2156 else if (error == &BAD_REFERENCE)
2168 error = xasprintf ("Table has %zu variables with circular or "
2169 "unresolved references, including variable %s.",
2170 n_nodes, nodes[0]->id);
2176 const struct spvdx_cross *cross = v->graph->faceting->cross;
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;
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;
2186 size_t max_layers = (v->graph->faceting->n_layers1
2187 + v->graph->faceting->n_layers2);
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;
2194 error = add_dimensions (&series_map, columns, PIVOT_AXIS_COLUMN, v, table,
2195 dim_series, &n_dim_series, 1);
2199 error = add_dimensions (&series_map, rows, PIVOT_AXIS_ROW, v, table,
2200 dim_series, &n_dim_series, max_columns + 1);
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);
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
2219 struct spv_series *cell = spv_series_find (&series_map, "cell");
2222 error = xstrdup (_("Table lacks cell data."));
2226 struct spv_series *cell_format = parse_formatting (v, &series_map,
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++)
2233 for (size_t j = 0; j < table->n_dimensions; j++)
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);
2240 dim_indexes[j] = cat->data_index;
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);
2252 const struct spv_data_value *d = &footnotes->values[i];
2255 const char *p = d->s;
2259 int idx = strtol (p, &tail, 10);
2260 add_footnote (value, idx, table);
2270 if (value->type == PIVOT_VALUE_NUMERIC
2271 && value->numeric.x == SYSMIS
2272 && !value->n_footnotes)
2274 /* Apparently, system-missing values are just empty cells? */
2275 pivot_value_destroy (value);
2278 pivot_table_put (table, dim_indexes, table->n_dimensions, value);
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);
2288 pivot_table_assign_label_depth (table);
2290 format_map_destroy (&format_map);
2294 spv_series_destroy (&series_map);
2297 pivot_table_unref (table);