spv-legacy-decoder: Always give the pivot table a title.
[pspp] / src / output / spv / spv-legacy-decoder.c
index 6a8c2d082fd09b2b102e5e5ac37cb18a4ebfaa4c..e103dd842e8247379cbfc670803c24bc9f3a8f83 100644 (file)
@@ -35,6 +35,7 @@
 #include "output/pivot-table.h"
 #include "output/spv/detail-xml-parser.h"
 #include "output/spv/spv-legacy-data.h"
+#include "output/spv/spv-table-look.h"
 #include "output/spv/spv.h"
 #include "output/spv/structure-xml-parser.h"
 
 #define N_(msgid) msgid
 #define _(msgid) gettext (msgid)
 
-struct spv_legacy_properties
-  {
-    /* General properties. */
-    bool omit_empty;
-    int width_ranges[TABLE_N_AXES][2];      /* In 1/96" units. */
-    bool row_labels_in_corner;
-
-    /* Footnote display settings. */
-    bool show_numeric_markers;
-    bool footnote_marker_superscripts;
-
-    /* Styles. */
-    struct area_style areas[PIVOT_N_AREAS];
-    struct table_border_style borders[PIVOT_N_BORDERS];
-
-    /* Print settings. */
-    bool print_all_layers;
-    bool paginate_layers;
-    bool shrink_to_width;
-    bool shrink_to_length;
-    bool top_continuation, bottom_continuation;
-    char *continuation;
-    size_t n_orphan_lines;
-  };
-
 struct spv_series
   {
     struct hmap_node hmap_node; /* By name. */
@@ -227,7 +203,9 @@ spv_map_insert (struct hmap *map, double from, const char *to,
       else
         {
           union value v = { .f = mapping->to.d };
-          mapping->to.s = data_out_stretchy (&v, NULL, format, NULL);
+          mapping->to.s = data_out_stretchy (&v, NULL, format,
+                                             settings_get_fmt_settings (),
+                                             NULL);
           mapping->to.width = strlen (mapping->to.s);
         }
     }
@@ -299,7 +277,7 @@ spv_series_parse_value_map_entry (struct hmap *map,
                           vme->from);
 
       char *error = spv_map_insert (map, from, vme->to, true,
-                                    &(struct fmt_spec) { FMT_A, 40, 0 });
+                                    &(struct fmt_spec) { .type = FMT_A, .w = 40 });
       if (error)
         return error;
 
@@ -389,7 +367,7 @@ decode_number_format (const struct spvdx_number_format *nf)
   if (d < 0 || d > 15)
     d = 2;
 
-  struct fmt_spec f = (struct fmt_spec) { type, 40, d };
+  struct fmt_spec f = (struct fmt_spec) { .type = type, .w = 40, .d = d };
   fmt_fix_output (&f);
   return f;
 }
@@ -599,16 +577,10 @@ optional_px (double inches, int default_px)
   return inches != DBL_MAX ? inches * 96.0 : default_px;
 }
 
-static int
-optional_pt (double inches, int default_pt)
-{
-  return inches != DBL_MAX ? inches * 72.0 + .5 : default_pt;
-}
-
 static void
 decode_spvdx_style_incremental (const struct spvdx_style *in,
                                 const struct spvdx_style *bg,
-                                struct area_style *out)
+                                struct table_area_style *out)
 {
   if (in && in->font_weight)
     out->font_style.bold = in->font_weight == SPVDX_FONT_WEIGHT_BOLD;
@@ -674,9 +646,9 @@ decode_spvdx_style_incremental (const struct spvdx_style *in,
 static void
 decode_spvdx_style (const struct spvdx_style *in,
                     const struct spvdx_style *bg,
-                    struct area_style *out)
+                    struct table_area_style *out)
 {
-  *out = (struct area_style) AREA_STYLE_INITIALIZER;
+  *out = (struct table_area_style) TABLE_AREA_STYLE_INITIALIZER;
   decode_spvdx_style_incremental (in, bg, out);
 }
 
@@ -697,16 +669,16 @@ decode_label_frame (struct pivot_table *table,
     return NULL;
 
   struct pivot_value **target;
-  struct area_style *area;
+  struct table_area_style *area;
   if (lf->label->purpose == SPVDX_PURPOSE_TITLE)
     {
       target = &table->title;
-      area = &table->areas[PIVOT_AREA_TITLE];
+      area = &table->look->areas[PIVOT_AREA_TITLE];
     }
   else if (lf->label->purpose == SPVDX_PURPOSE_SUB_TITLE)
     {
       target = &table->caption;
-      area = &table->areas[PIVOT_AREA_CAPTION];
+      area = &table->look->areas[PIVOT_AREA_CAPTION];
     }
   else if (lf->label->purpose == SPVDX_PURPOSE_FOOTNOTE)
     {
@@ -714,7 +686,7 @@ decode_label_frame (struct pivot_table *table,
           && lf->label->text[0]->uses_reference != INT_MIN)
         {
           target = NULL;
-          area = &table->areas[PIVOT_AREA_FOOTER];
+          area = &table->look->areas[PIVOT_AREA_FOOTER];
         }
       else
         return NULL;
@@ -722,17 +694,17 @@ decode_label_frame (struct pivot_table *table,
   else if (lf->label->purpose == SPVDX_PURPOSE_LAYER)
     {
       target = NULL;
-      area = &table->areas[PIVOT_AREA_LAYERS];
+      area = &table->look->areas[PIVOT_AREA_LAYERS];
     }
   else
     return NULL;
 
-  area_style_uninit (area);
+  table_area_style_uninit (area);
   decode_spvdx_style (lf->label->style, lf->label->text_frame_style, area);
 
   if (target)
     {
-      struct pivot_value *value = xzalloc (sizeof *value);
+      struct pivot_value *value = XZALLOC (struct pivot_value);
       value->type = PIVOT_VALUE_TEXT;
       for (size_t i = 0; i < lf->label->n_text; i++)
         {
@@ -748,6 +720,9 @@ decode_label_frame (struct pivot_table *table,
               value->text.local = new;
             }
         }
+      if (!value->text.local)
+        value->text.local = xstrdup ("");
+      value->text.c = value->text.id = value->text.local;
       pivot_value_destroy (*target);
       *target = value;
     }
@@ -786,7 +761,7 @@ static char BAD_REFERENCE;
 
 static char * WARN_UNUSED_RESULT
 decode_spvdx_source_variable (const struct spvxml_node *node,
-                              struct spv_data *data,
+                              const struct spv_data *data,
                               struct hmap *series_map)
 {
   const struct spvdx_source_variable *sv = spvdx_cast_source_variable (node);
@@ -809,10 +784,10 @@ decode_spvdx_source_variable (const struct spvxml_node *node,
                       "source %s variable %s.",
                       sv->node_.id, sv->source, sv->source_name);
 
-  struct spv_series *s = xzalloc (sizeof *s);
+  struct spv_series *s = XZALLOC (struct spv_series);
   s->name = xstrdup (node->id);
   s->xml = node;
-  s->label = sv->label ? xstrdup (sv->label) : NULL;
+  s->label = xstrdup_if_nonnull (sv->label);
   s->label_series = label_series;
   s->values = spv_data_values_clone (var->values, var->n_values);
   s->n_values = var->n_values;
@@ -833,7 +808,8 @@ decode_spvdx_source_variable (const struct spvxml_node *node,
             if (label_series->values[i].width < 0)
               {
                 union value v = { .f = label_series->values[i].d };
-                dest = data_out_stretchy (&v, "UTF-8", &s->format, NULL);
+                dest = data_out_stretchy (&v, "UTF-8", &s->format,
+                                          settings_get_fmt_settings (), NULL);
               }
             else
               dest = label_series->values[i].s;
@@ -865,7 +841,7 @@ decode_spvdx_derived_variable (const struct spvxml_node *node,
         return &BAD_REFERENCE;
 
       n_values = existing_series->n_values;
-      values = xcalloc (n_values, sizeof *values);
+      values = XCALLOC (n_values, struct spv_data_value);
       for (size_t i = 0; i < n_values; i++)
         values[i].width = -1;
     }
@@ -893,7 +869,7 @@ decode_spvdx_derived_variable (const struct spvxml_node *node,
     return xasprintf ("Derived variable %s has unknown value \"%s\"",
                       node->id, dv->value);
 
-  struct spv_series *s = xzalloc (sizeof *s);
+  struct spv_series *s = XZALLOC (struct spv_series);
   s->format = F_8_0;
   s->name = xstrdup (node->id);
   s->values = values;
@@ -983,7 +959,7 @@ pivot_value_from_data_value (const struct spv_data_value *data,
   if (error)
     return error;
 
-  struct pivot_value *v = xzalloc (sizeof *v);
+  struct pivot_value *v = XZALLOC (struct pivot_value);
   if (data->width >= 0)
     {
       if (format && fmt_get_category (f.type) == FMT_CAT_DATE)
@@ -995,8 +971,8 @@ pivot_value_from_data_value (const struct spv_data_value *data,
               && len == 23
               && data->s[len] == '\0')
             {
-              double date = calendar_gregorian_to_offset (year, month, day,
-                                                          NULL);
+              double date = calendar_gregorian_to_offset (
+                year, month, day, settings_get_fmt_settings (), NULL);
               if (date != SYSMIS)
                 {
                   v->type = PIVOT_VALUE_NUMERIC;
@@ -1109,14 +1085,15 @@ add_dimension (struct spv_series **series, size_t n,
     = find_facet_level (v, base_facet_level + n);
   if (fl)
     {
-      struct area_style *area = (axis_type == PIVOT_AXIS_COLUMN
-                                 ? &table->areas[PIVOT_AREA_COLUMN_LABELS]
-                                 : axis_type == PIVOT_AXIS_ROW
-                                 ? &table->areas[PIVOT_AREA_ROW_LABELS]
-                                 : NULL);
+      struct table_area_style *area
+        = (axis_type == PIVOT_AXIS_COLUMN
+           ? &table->look->areas[PIVOT_AREA_COLUMN_LABELS]
+           : axis_type == PIVOT_AXIS_ROW
+           ? &table->look->areas[PIVOT_AREA_ROW_LABELS]
+           : NULL);
       if (area && fl->axis->label)
         {
-          area_style_uninit (area);
+          table_area_style_uninit (area);
           decode_spvdx_style (fl->axis->label->style,
                               fl->axis->label->text_frame_style, area);
         }
@@ -1130,7 +1107,7 @@ add_dimension (struct spv_series **series, size_t n,
         decode_spvdx_style_incremental (
           fl2->axis->major_ticks->style,
           fl2->axis->major_ticks->tick_frame_style,
-          &table->areas[PIVOT_AREA_ROW_LABELS]);
+          &table->look->areas[PIVOT_AREA_ROW_LABELS]);
     }
 
   const struct spvdx_facet_level *fl3 = find_facet_level (v, base_facet_level);
@@ -1167,7 +1144,7 @@ add_dimension (struct spv_series **series, size_t n,
   assert (n_cats > 0);
 
   /* Make the categories. */
-  struct pivot_dimension *d = xzalloc (sizeof *d);
+  struct pivot_dimension *d = XZALLOC (struct pivot_dimension);
   table->dimensions[table->n_dimensions++] = d;
 
   series[0]->n_index = max_cat + 1;
@@ -1178,7 +1155,7 @@ add_dimension (struct spv_series **series, size_t n,
     {
       struct spv_data_value *dv = &series[0]->values[cat_rows[k]];
       int dv_num = dv ? dv->d : dv->index;
-      struct pivot_category *cat = xzalloc (sizeof *cat);
+      struct pivot_category *cat = XZALLOC (struct pivot_category);
       char *retval = pivot_value_from_data_value (
         spv_map_lookup (&series[0]->map, dv), NULL, NULL, &cat->name);
       if (retval)
@@ -1298,6 +1275,11 @@ add_dimension (struct spv_series **series, size_t n,
   /* Now drop unnamed 1-category groups and add parent pointers. */
   for (size_t j = 0; j < n_cats; j++)
     add_parents (cats[j], d->root, j);
+  for (size_t j = 0; j < d->n_leaves; j++)
+    {
+      d->data_leaves[j]->data_index = j;
+      d->presentation_leaves[j]->presentation_index = j;
+    }
 
   d->root->subs = cats;
   d->root->n_subs = n_cats;
@@ -1417,65 +1399,6 @@ add_layers (struct hmap *series_map,
   return NULL;
 }
 
-static int
-optional_int (int x, int default_value)
-{
-  return x != INT_MIN ? x : default_value;
-}
-
-static enum pivot_area
-pivot_area_from_name (const char *name)
-{
-  static const char *area_names[PIVOT_N_AREAS] = {
-    [PIVOT_AREA_TITLE] = "title",
-    [PIVOT_AREA_CAPTION] = "caption",
-    [PIVOT_AREA_FOOTER] = "footnotes",
-    [PIVOT_AREA_CORNER] = "cornerLabels",
-    [PIVOT_AREA_COLUMN_LABELS] = "columnLabels",
-    [PIVOT_AREA_ROW_LABELS] = "rowLabels",
-    [PIVOT_AREA_DATA] = "data",
-    [PIVOT_AREA_LAYERS] = "layers",
-  };
-
-  enum pivot_area area;
-  for (area = 0; area < PIVOT_N_AREAS; area++)
-    if (!strcmp (name, area_names[area]))
-      break;
-  return area;
-}
-
-static enum pivot_border
-pivot_border_from_name (const char *name)
-{
-  static const char *border_names[PIVOT_N_BORDERS] = {
-    [PIVOT_BORDER_TITLE] = "titleLayerSeparator",
-    [PIVOT_BORDER_OUTER_LEFT] = "leftOuterFrame",
-    [PIVOT_BORDER_OUTER_TOP] = "topOuterFrame",
-    [PIVOT_BORDER_OUTER_RIGHT] = "rightOuterFrame",
-    [PIVOT_BORDER_OUTER_BOTTOM] = "bottomOuterFrame",
-    [PIVOT_BORDER_INNER_LEFT] = "leftInnerFrame",
-    [PIVOT_BORDER_INNER_TOP] = "topInnerFrame",
-    [PIVOT_BORDER_INNER_RIGHT] = "rightInnerFrame",
-    [PIVOT_BORDER_INNER_BOTTOM] = "bottomInnerFrame",
-    [PIVOT_BORDER_DATA_LEFT] = "dataAreaLeft",
-    [PIVOT_BORDER_DATA_TOP] = "dataAreaTop",
-    [PIVOT_BORDER_DIM_ROW_HORZ] = "horizontalDimensionBorderRows",
-    [PIVOT_BORDER_DIM_ROW_VERT] = "verticalDimensionBorderRows",
-    [PIVOT_BORDER_DIM_COL_HORZ] = "horizontalDimensionBorderColumns",
-    [PIVOT_BORDER_DIM_COL_VERT] = "verticalDimensionBorderColumns",
-    [PIVOT_BORDER_CAT_ROW_HORZ] = "horizontalCategoryBorderRows",
-    [PIVOT_BORDER_CAT_ROW_VERT] = "verticalCategoryBorderRows",
-    [PIVOT_BORDER_CAT_COL_HORZ] = "horizontalCategoryBorderColumns",
-    [PIVOT_BORDER_CAT_COL_VERT] = "verticalCategoryBorderColumns",
-  };
-
-  enum pivot_border border;
-  for (border = 0; border < PIVOT_N_BORDERS; border++)
-    if (!strcmp (name, border_names[border]))
-      break;
-  return border;
-}
-
 static struct pivot_category *
 find_category (struct spv_series *series, int index)
 {
@@ -1498,17 +1421,17 @@ static void
 apply_styles_to_value (struct pivot_table *table,
                        struct pivot_value *value,
                        const struct spvdx_set_format *sf,
-                       const struct area_style *base_area_style,
+                       const struct table_area_style *base_area_style,
                        const struct spvdx_style *fg,
                        const struct spvdx_style *bg)
 {
   if (sf)
     {
-      if (sf->reset > 0)
+      if (sf->reset > 0 && value->ex)
         {
-          free (value->footnotes);
-          value->footnotes = NULL;
-          value->n_footnotes = 0;
+          free (value->ex->footnote_indexes);
+          value->ex->footnote_indexes = NULL;
+          value->ex->n_footnotes = 0;
         }
 
       struct fmt_spec format = { .w = 0 };
@@ -1553,15 +1476,16 @@ apply_styles_to_value (struct pivot_table *table,
     }
   if (fg || bg)
     {
-      struct area_style area;
+      const struct pivot_value_ex *ex = pivot_value_ex (value);
+      struct table_area_style area;
       pivot_value_get_style (
         value,
-        value->font_style ? value->font_style : &base_area_style->font_style,
-        value->cell_style ? value->cell_style : &base_area_style->cell_style,
+        ex->font_style ? ex->font_style : &base_area_style->font_style,
+        ex->cell_style ? ex->cell_style : &base_area_style->cell_style,
         &area);
       decode_spvdx_style_incremental (fg, bg, &area);
       pivot_value_set_style (value, &area);
-      area_style_uninit (&area);
+      table_area_style_uninit (&area);
     }
 }
 
@@ -1580,13 +1504,13 @@ decode_set_cell_properties__ (struct pivot_table *table,
       && !interval && !major_ticks && !frame && !set_format)
     {
       /* Sets alt_fg_color and alt_bg_color. */
-      struct area_style area;
+      struct table_area_style area;
       decode_spvdx_style (labeling, graph, &area);
-      table->areas[PIVOT_AREA_DATA].font_style.fg[1]
+      table->look->areas[PIVOT_AREA_DATA].font_style.fg[1]
         = area.font_style.fg[0];
-      table->areas[PIVOT_AREA_DATA].font_style.bg[1]
+      table->look->areas[PIVOT_AREA_DATA].font_style.bg[1]
         = area.font_style.bg[0];
-      area_style_uninit (&area);
+      table_area_style_uninit (&area);
     }
   else if (graph
            && !labeling && !interval && !major_ticks && !frame && !set_format)
@@ -1618,10 +1542,10 @@ decode_set_cell_properties__ (struct pivot_table *table,
           struct pivot_category *c = find_category (s, include);
           if (c)
             {
-              const struct area_style *base_area_style
+              const struct table_area_style *base_area_style
                 = (c->dimension->axis_type == PIVOT_AXIS_ROW
-                   ? &table->areas[PIVOT_AREA_ROW_LABELS]
-                   : &table->areas[PIVOT_AREA_COLUMN_LABELS]);
+                   ? &table->look->areas[PIVOT_AREA_ROW_LABELS]
+                   : &table->look->areas[PIVOT_AREA_COLUMN_LABELS]);
               apply_styles_to_value (table, c->name, set_format,
                                      base_area_style, major_ticks, frame);
             }
@@ -1638,9 +1562,9 @@ decode_set_cell_properties__ (struct pivot_table *table,
     {
       /* Formatting for individual cells or groups of them with some dimensions
          in common. */
-      int **indexes = xcalloc (table->n_dimensions, sizeof *indexes);
-      size_t *n = xcalloc (table->n_dimensions, sizeof *n);
-      size_t *allocated = xcalloc (table->n_dimensions, sizeof *allocated);
+      int **indexes = XCALLOC (table->n_dimensions, int *);
+      size_t *n = XCALLOC (table->n_dimensions, size_t);
+      size_t *allocated = XCALLOC (table->n_dimensions, size_t);
 
       for (size_t i = 0; i < intersect->n_where; i++)
         {
@@ -1714,7 +1638,7 @@ decode_set_cell_properties__ (struct pivot_table *table,
                 goto skip;
             }
           apply_styles_to_value (table, cell->value, set_format,
-                                 &table->areas[PIVOT_AREA_DATA],
+                                 &table->look->areas[PIVOT_AREA_DATA],
                                  labeling, interval);
 
         skip: ;
@@ -1785,7 +1709,7 @@ decode_set_cell_properties (struct pivot_table *table, struct hmap *series_map,
               struct pivot_cell *cell;
               HMAP_FOR_EACH (cell, struct pivot_cell, hmap_node, &table->cells)
                 apply_styles_to_value (table, cell->value, set_format,
-                                       &table->areas[PIVOT_AREA_DATA],
+                                       &table->look->areas[PIVOT_AREA_DATA],
                                        NULL, NULL);
             }
         }
@@ -1798,177 +1722,6 @@ decode_set_cell_properties (struct pivot_table *table, struct hmap *series_map,
     }
 }
 
-char * WARN_UNUSED_RESULT
-decode_spvsx_legacy_properties (const struct spvsx_table_properties *in,
-                                struct spv_legacy_properties **outp)
-{
-  struct spv_legacy_properties *out = xzalloc (sizeof *out);
-  char *error;
-
-  if (!in)
-    {
-      error = xstrdup ("Legacy table lacks tableProperties");
-      goto error;
-    }
-
-  const struct spvsx_general_properties *g = in->general_properties;
-  out->omit_empty = g->hide_empty_rows != 0;
-  out->width_ranges[TABLE_HORZ][0] = optional_pt (g->minimum_column_width, -1);
-  out->width_ranges[TABLE_HORZ][1] = optional_pt (g->maximum_column_width, -1);
-  out->width_ranges[TABLE_VERT][0] = optional_pt (g->minimum_row_width, -1);
-  out->width_ranges[TABLE_VERT][1] = optional_pt (g->maximum_row_width, -1);
-  out->row_labels_in_corner
-    = g->row_dimension_labels != SPVSX_ROW_DIMENSION_LABELS_NESTED;
-
-  const struct spvsx_footnote_properties *f = in->footnote_properties;
-  out->footnote_marker_superscripts
-    = (f->marker_position != SPVSX_MARKER_POSITION_SUBSCRIPT);
-  out->show_numeric_markers
-    = (f->number_format == SPVSX_NUMBER_FORMAT_NUMERIC);
-
-  for (int i = 0; i < PIVOT_N_AREAS; i++)
-    area_style_copy (NULL, &out->areas[i], pivot_area_get_default_style (i));
-
-  const struct spvsx_cell_format_properties *cfp = in->cell_format_properties;
-  for (size_t i = 0; i < cfp->n_cell_style; i++)
-    {
-      const struct spvsx_cell_style *c = cfp->cell_style[i];
-      const char *name = CHAR_CAST (const char *, c->node_.raw->name);
-      enum pivot_area area = pivot_area_from_name (name);
-      if (area == PIVOT_N_AREAS)
-        {
-          error = xasprintf ("unknown area \"%s\" in cellFormatProperties",
-                             name);
-          goto error;
-        }
-
-      struct area_style *a = &out->areas[area];
-      const struct spvsx_style *s = c->style;
-      if (s->font_weight)
-        a->font_style.bold = s->font_weight == SPVSX_FONT_WEIGHT_BOLD;
-      if (s->font_style)
-        a->font_style.italic = s->font_style == SPVSX_FONT_STYLE_ITALIC;
-      a->font_style.underline = false;
-      if (s->color >= 0)
-        a->font_style.fg[0] = optional_color (
-          s->color, (struct cell_color) CELL_COLOR_BLACK);
-      if (c->alternating_text_color >= 0 || s->color >= 0)
-        a->font_style.fg[1] = optional_color (c->alternating_text_color,
-                                              a->font_style.fg[0]);
-      if (s->color2 >= 0)
-        a->font_style.bg[0] = optional_color (
-          s->color2, (struct cell_color) CELL_COLOR_WHITE);
-      if (c->alternating_color >= 0 || s->color2 >= 0)
-        a->font_style.bg[1] = optional_color (c->alternating_color,
-                                              a->font_style.bg[0]);
-      if (s->font_family)
-        {
-          free (a->font_style.typeface);
-          a->font_style.typeface = xstrdup (s->font_family);
-        }
-
-      if (s->font_size)
-        a->font_style.size = optional_length (s->font_size, 0);
-
-      if (s->text_alignment)
-        a->cell_style.halign
-          = (s->text_alignment == SPVSX_TEXT_ALIGNMENT_LEFT
-             ? TABLE_HALIGN_LEFT
-             : s->text_alignment == SPVSX_TEXT_ALIGNMENT_RIGHT
-             ? TABLE_HALIGN_RIGHT
-             : s->text_alignment == SPVSX_TEXT_ALIGNMENT_CENTER
-             ? TABLE_HALIGN_CENTER
-             : s->text_alignment == SPVSX_TEXT_ALIGNMENT_DECIMAL
-             ? TABLE_HALIGN_DECIMAL
-             : TABLE_HALIGN_MIXED);
-      if (s->label_location_vertical)
-        a->cell_style.valign
-          = (s->label_location_vertical == SPVSX_LABEL_LOCATION_VERTICAL_NEGATIVE
-             ? TABLE_VALIGN_BOTTOM
-             : s->label_location_vertical == SPVSX_LABEL_LOCATION_VERTICAL_POSITIVE
-             ? TABLE_VALIGN_TOP
-             : TABLE_VALIGN_CENTER);
-
-      if (s->decimal_offset != DBL_MAX)
-        a->cell_style.decimal_offset = optional_px (s->decimal_offset, 0);
-
-      if (s->margin_left != DBL_MAX)
-        a->cell_style.margin[TABLE_HORZ][0] = optional_px (s->margin_left, 8);
-      if (s->margin_right != DBL_MAX)
-        a->cell_style.margin[TABLE_HORZ][1] = optional_px (s->margin_right,
-                                                           11);
-      if (s->margin_top != DBL_MAX)
-        a->cell_style.margin[TABLE_VERT][0] = optional_px (s->margin_top, 1);
-      if (s->margin_bottom != DBL_MAX)
-        a->cell_style.margin[TABLE_VERT][1] = optional_px (s->margin_bottom,
-                                                           1);
-    }
-
-  for (int i = 0; i < PIVOT_N_BORDERS; i++)
-    pivot_border_get_default_style (i, &out->borders[i]);
-
-  const struct spvsx_border_properties *bp = in->border_properties;
-  for (size_t i = 0; i < bp->n_border_style; i++)
-    {
-      const struct spvsx_border_style *bin = bp->border_style[i];
-      const char *name = CHAR_CAST (const char *, bin->node_.raw->name);
-      enum pivot_border border = pivot_border_from_name (name);
-      if (border == PIVOT_N_BORDERS)
-        {
-          error = xasprintf ("unknown border \"%s\" parsing borderProperties",
-                             name);
-          goto error;
-        }
-
-      struct table_border_style *bout = &out->borders[border];
-      bout->stroke
-        = (bin->border_style_type == SPVSX_BORDER_STYLE_TYPE_NONE
-           ? TABLE_STROKE_NONE
-           : bin->border_style_type == SPVSX_BORDER_STYLE_TYPE_DASHED
-           ? TABLE_STROKE_DASHED
-           : bin->border_style_type == SPVSX_BORDER_STYLE_TYPE_THICK
-           ? TABLE_STROKE_THICK
-           : bin->border_style_type == SPVSX_BORDER_STYLE_TYPE_THIN
-           ? TABLE_STROKE_THIN
-           : bin->border_style_type == SPVSX_BORDER_STYLE_TYPE_DOUBLE
-           ? TABLE_STROKE_DOUBLE
-           : TABLE_STROKE_SOLID);
-      bout->color = optional_color (bin->color,
-                                    (struct cell_color) CELL_COLOR_BLACK);
-    }
-
-  const struct spvsx_printing_properties *pp = in->printing_properties;
-  out->print_all_layers = pp->print_all_layers > 0;
-  out->paginate_layers = pp->print_each_layer_on_separate_page > 0;
-  out->shrink_to_width = pp->rescale_wide_table_to_fit_page > 0;
-  out->shrink_to_length = pp->rescale_long_table_to_fit_page > 0;
-  out->top_continuation = pp->continuation_text_at_top > 0;
-  out->bottom_continuation = pp->continuation_text_at_bottom > 0;
-  out->continuation = xstrdup (pp->continuation_text
-                               ? pp->continuation_text : "(cont.)");
-  out->n_orphan_lines = optional_int (pp->window_orphan_lines, 2);
-
-  *outp = out;
-  return NULL;
-
-error:
-  spv_legacy_properties_destroy (out);
-  *outp = NULL;
-  return error;
-}
-
-void
-spv_legacy_properties_destroy (struct spv_legacy_properties *props)
-{
-  if (props)
-    {
-      for (size_t i = 0; i < PIVOT_N_AREAS; i++)
-        area_style_uninit (&props->areas[i]);
-      free (props->continuation);
-      free (props);
-    }
-}
-
 static struct spv_series *
 parse_formatting (const struct spvdx_visualization *v,
                   const struct hmap *series_map, struct hmap *format_map)
@@ -2015,44 +1768,20 @@ format_map_destroy (struct hmap *format_map)
 
 char * WARN_UNUSED_RESULT
 decode_spvdx_table (const struct spvdx_visualization *v, const char *subtype,
-                    const struct spv_legacy_properties *props,
-                    struct spv_data *data, struct pivot_table **outp)
+                    const struct pivot_table_look *look,
+                    const struct spv_data *data, struct pivot_table **outp)
 {
-  struct pivot_table *table = pivot_table_create__ (NULL, subtype);
+  struct pivot_table *table = pivot_table_create__ (
+    pivot_value_new_user_text (v->name, SIZE_MAX), subtype);
+
+  pivot_table_set_look (table, look);
+  table->look = pivot_table_look_unshare (table->look);
 
   struct hmap series_map = HMAP_INITIALIZER (series_map);
   struct hmap format_map = HMAP_INITIALIZER (format_map);
   struct spv_series **dim_series = NULL;
   char *error;
 
-  /* First get the legacy properties. */
-  table->omit_empty = props->omit_empty;
-  for (enum table_axis axis = 0; axis < TABLE_N_AXES; axis++)
-    for (int i = 0; i < 2; i++)
-      if (props->width_ranges[axis][i] > 0)
-        table->sizing[axis].range[i] = props->width_ranges[axis][i];
-  table->row_labels_in_corner = props->row_labels_in_corner;
-
-  table->footnote_marker_superscripts = props->footnote_marker_superscripts;
-  table->show_numeric_markers = props->show_numeric_markers;
-
-  for (size_t i = 0; i < PIVOT_N_AREAS; i++)
-    {
-      area_style_uninit (&table->areas[i]);
-      area_style_copy (NULL, &table->areas[i], &props->areas[i]);
-    }
-  for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
-    table->borders[i] = props->borders[i];
-
-  table->print_all_layers = props->print_all_layers;
-  table->paginate_layers = props->paginate_layers;
-  table->shrink_to_fit[TABLE_HORZ] = props->shrink_to_width;
-  table->shrink_to_fit[TABLE_VERT] = props->shrink_to_length;
-  table->top_continuation = props->top_continuation;
-  table->bottom_continuation = props->bottom_continuation;
-  table->continuation = xstrdup (props->continuation);
-  table->n_orphan_lines = props->n_orphan_lines;
-
   struct spvdx_visualization_extension *ve = v->visualization_extension;
   table->show_grid_lines = ve && ve->show_gridline;
 
@@ -2064,8 +1793,8 @@ decode_spvdx_table (const struct spvdx_visualization *v, const char *subtype,
                   &min_width, &max_width, &n)
           && v->graph->cell_style->width[n] == '\0')
         {
-          table->sizing[TABLE_HORZ].range[0] = min_width;
-          table->sizing[TABLE_HORZ].range[1] = max_width;
+          table->look->width_ranges[TABLE_HORZ][0] = min_width;
+          table->look->width_ranges[TABLE_HORZ][1] = max_width;
         }
     }
 
@@ -2131,10 +1860,10 @@ decode_spvdx_table (const struct spvdx_visualization *v, const char *subtype,
       }
   if (v->graph->interval->labeling->style)
     {
-      area_style_uninit (&table->areas[PIVOT_AREA_DATA]);
+      table_area_style_uninit (&table->look->areas[PIVOT_AREA_DATA]);
       decode_spvdx_style (v->graph->interval->labeling->style,
                           v->graph->cell_style,
-                          &table->areas[PIVOT_AREA_DATA]);
+                          &table->look->areas[PIVOT_AREA_DATA]);
     }
 
   /* Decode all of the sourceVariable and derivedVariable  */
@@ -2269,7 +1998,7 @@ decode_spvdx_table (const struct spvdx_visualization *v, const char *subtype,
 
       if (value->type == PIVOT_VALUE_NUMERIC
           && value->numeric.x == SYSMIS
-          && !value->n_footnotes)
+          && !pivot_value_ex (value)->n_footnotes)
         {
           /* Apparently, system-missing values are just empty cells? */
           pivot_value_destroy (value);