pspp-output: New option --table-look.
authorBen Pfaff <blp@cs.stanford.edu>
Tue, 27 Oct 2020 02:31:18 +0000 (19:31 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Tue, 27 Oct 2020 02:31:18 +0000 (19:31 -0700)
13 files changed:
NEWS
doc/pspp-output.texi
src/output/spv/automake.mk
src/output/spv/spv-legacy-decoder.c
src/output/spv/spv-legacy-decoder.h
src/output/spv/spv-select.c
src/output/spv/spv-table-look.c [new file with mode: 0644]
src/output/spv/spv-table-look.h [new file with mode: 0644]
src/output/spv/spv.c
src/output/spv/spv.h
src/output/spv/structure-xml.grammar
utilities/pspp-output.1
utilities/pspp-output.c

diff --git a/NEWS b/NEWS
index 00b3c38e8c3dbab8f07acb51e8b80038e00fe061..a2f3a864ebd003f0d459ca6d2c5a7e9ac4c38ff4 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -17,7 +17,7 @@ Changes from 1.4.1 to 1.5.2:
    The new interface provides the user with a preview of the data to be imported
    and interactive methods to select the desired ranges.
 
- * The pspp-output utility has a new --nth-commands option.
+ * The pspp-output utility has new --table-look and --nth-commands options.
 
 Changes from 1.4.0 to 1.4.1:
 
index 29c625e0cd6cf1e0e5f15f2881dc395113130e08..35cc6004630e54a4139d74438ac2ddd972f79a72 100644 (file)
@@ -110,6 +110,10 @@ details of the available output options.
 By default, if the source is corrupt or otherwise cannot be processed,
 the destination is not written.  With @option{-F} or @option{--force},
 the destination is written as best it can, even with errors.
+
+@item --table-look=@var{file}
+Reads a table style from @var{file} and applies it to all of the
+output tables.  The file should a TableLook @file{.stt} file.
 @end table
 
 @node Input Selection Options
index 37f98c3f131bd03e606439ed4fb511e30fb2e65e..970c821318e15e0e2f3cdc6df685a9ea4c453a78 100644 (file)
@@ -30,6 +30,8 @@ src_output_liboutput_la_SOURCES += \
        src/output/spv/spv-output.h \
        src/output/spv/spv-select.c \
        src/output/spv/spv-select.h \
+       src/output/spv/spv-table-look.c \
+       src/output/spv/spv-table-look.h \
        src/output/spv/spv-writer.c \
        src/output/spv/spv-writer.h \
        src/output/spv/spv.c \
index 211813ac1b706dee88b43f452b1108546aabad7f..b5f4e9eeacb9285af5fbb6ccb5c720f50740d274 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. */
@@ -599,12 +575,6 @@ 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,
@@ -1417,65 +1387,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)
 {
@@ -1798,177 +1709,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 +1755,17 @@ 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,
+                    const struct spv_table_look *look,
                     struct spv_data *data, struct pivot_table **outp)
 {
   struct pivot_table *table = pivot_table_create__ (NULL, subtype);
+  spv_table_look_install (look, table);
 
   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;
 
index 9ff2428b479427ac52582e10743d195a7c99a310..2cf9d6b10f68e2890bf8cb01b57defdf84f732b7 100644 (file)
@@ -17,7 +17,7 @@
 #ifndef OUTPUT_SPV_LEGACY_DECODER_H
 #define OUTPUT_SPV_LEGACY_DECODER_H 1
 
-/* SPSS Viewer (SPV) legacy binary decoder.
+/* SPSS Viewer (SPV) legacy decoder.
 
    Used by spv.h, not useful directly. */
 
 
 struct pivot_table;
 struct spvdx_visualization;
-struct spvsx_table_properties;
 struct spv_data;
-
-struct spv_legacy_properties;
-
-void spv_legacy_properties_destroy (struct spv_legacy_properties *);
-
-char *decode_spvsx_legacy_properties (const struct spvsx_table_properties *,
-                                      struct spv_legacy_properties **)
-  WARN_UNUSED_RESULT;
+struct spv_table_look;
 
 char *decode_spvdx_table (const struct spvdx_visualization *,
                           const char *subtype,
-                          const struct spv_legacy_properties *,
+                          const struct spv_table_look *,
                           struct spv_data *,
                           struct pivot_table **outp)
   WARN_UNUSED_RESULT;
index 6be9742795311a225be2b00df14d927238b1fa79..21b560d72080f7830765c75376564176eda3bc8a 100644 (file)
 #include "gl/c-ctype.h"
 #include "gl/xalloc.h"
 
+/* Returns true if ITEM represents a command, false otherwise.
+
+   The root item and each of its immediate children are considered to be
+   command items. */
 static bool
 is_command_item (const struct spv_item *item)
 {
diff --git a/src/output/spv/spv-table-look.c b/src/output/spv/spv-table-look.c
new file mode 100644 (file)
index 0000000..178c8ae
--- /dev/null
@@ -0,0 +1,343 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2017, 2018, 2020 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include "output/spv/spv-table-look.h"
+
+#include <errno.h>
+#include <libxml/xmlreader.h>
+#include <string.h>
+
+#include "output/spv/structure-xml-parser.h"
+
+#include "gl/read-file.h"
+#include "gl/xalloc.h"
+
+static struct cell_color
+optional_color (int color, struct cell_color default_color)
+{
+  return (color >= 0
+          ? (struct cell_color) CELL_COLOR (color >> 16, color >> 8, color)
+          : default_color);
+}
+
+static int
+optional_length (const char *s, int default_length)
+{
+  /* There is usually a "pt" suffix.  We ignore it. */
+  int length;
+  return s && sscanf (s, "%d", &length) == 1 ? length : default_length;
+}
+
+static int
+optional_px (double inches, int default_px)
+{
+  return inches != DBL_MAX ? inches * 96.0 : default_px;
+}
+
+static int
+optional_int (int x, int default_value)
+{
+  return x != INT_MIN ? x : default_value;
+}
+
+static int
+optional_pt (double inches, int default_pt)
+{
+  return inches != DBL_MAX ? inches * 72.0 + .5 : default_pt;
+}
+
+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;
+}
+
+char * WARN_UNUSED_RESULT
+spv_table_look_decode (const struct spvsx_table_properties *in,
+                       struct spv_table_look **outp)
+{
+  struct spv_table_look *out = xzalloc (sizeof *out);
+  char *error = NULL;
+
+  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_table_look_destroy (out);
+  *outp = NULL;
+  return error;
+}
+
+char * WARN_UNUSED_RESULT
+spv_table_look_read (const char *filename, struct spv_table_look **outp)
+{
+  *outp = NULL;
+
+  size_t length;
+  char *file = read_file (filename, 0, &length);
+  if (!file)
+    return xasprintf ("%s: failed to read file (%s)",
+                      filename, strerror (errno));
+
+  xmlDoc *doc = xmlReadMemory (file, length, NULL, NULL, XML_PARSE_NOBLANKS);
+  free (file);
+  if (!doc)
+    return xasprintf ("%s: failed to parse XML", filename);
+
+  struct spvxml_context ctx = SPVXML_CONTEXT_INIT (ctx);
+  struct spvsx_table_properties *tp;
+  spvsx_parse_table_properties (&ctx, xmlDocGetRootElement (doc), &tp);
+  char *error = spvxml_context_finish (&ctx, &tp->node_);
+
+  if (!error)
+    error = spv_table_look_decode (tp, outp);
+
+  spvsx_free_table_properties (tp);
+  xmlFreeDoc (doc);
+
+  return error;
+}
+
+void
+spv_table_look_destroy (struct spv_table_look *look)
+{
+  if (look)
+    {
+      for (size_t i = 0; i < PIVOT_N_AREAS; i++)
+        area_style_uninit (&look->areas[i]);
+      free (look->continuation);
+      free (look);
+    }
+}
+
+void
+spv_table_look_install (const struct spv_table_look *look,
+                        struct pivot_table *table)
+{
+  table->omit_empty = look->omit_empty;
+
+  for (enum table_axis axis = 0; axis < TABLE_N_AXES; axis++)
+    for (int i = 0; i < 2; i++)
+      if (look->width_ranges[axis][i] > 0)
+        table->sizing[axis].range[i] = look->width_ranges[axis][i];
+  table->row_labels_in_corner = look->row_labels_in_corner;
+
+  table->footnote_marker_superscripts = look->footnote_marker_superscripts;
+  table->show_numeric_markers = look->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], &look->areas[i]);
+    }
+  for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
+    table->borders[i] = look->borders[i];
+
+  table->print_all_layers = look->print_all_layers;
+  table->paginate_layers = look->paginate_layers;
+  table->shrink_to_fit[TABLE_HORZ] = look->shrink_to_width;
+  table->shrink_to_fit[TABLE_VERT] = look->shrink_to_length;
+  table->top_continuation = look->top_continuation;
+  table->bottom_continuation = look->bottom_continuation;
+  table->continuation = xstrdup (look->continuation);
+  table->n_orphan_lines = look->n_orphan_lines;
+}
diff --git a/src/output/spv/spv-table-look.h b/src/output/spv/spv-table-look.h
new file mode 100644 (file)
index 0000000..3e81757
--- /dev/null
@@ -0,0 +1,70 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2018, 2020 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_SPV_TABLE_LOOK_H
+#define OUTPUT_SPV_TABLE_LOOK_H 1
+
+/* TableLook decoder.
+
+   A TableLook specifies styles for tables and other aspects of output.  They
+   exist standalone as .stt files as well as embedded in structure XML, in
+   either case as a tableProperties element.
+*/
+
+#include <stdbool.h>
+#include "libpspp/compiler.h"
+#include "output/pivot-table.h"
+#include "output/table.h"
+
+struct spvsx_table_properties;
+
+struct spv_table_look
+  {
+    /* 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;
+  };
+
+void spv_table_look_destroy (struct spv_table_look *);
+
+char *spv_table_look_decode (const struct spvsx_table_properties *,
+                             struct spv_table_look **)
+  WARN_UNUSED_RESULT;
+char *spv_table_look_read (const char *, struct spv_table_look **)
+  WARN_UNUSED_RESULT;
+
+void spv_table_look_install (const struct spv_table_look *,
+                             struct pivot_table *);
+
+#endif /* output/spv/spv-table-look.h */
index 25fb962aaef7223516093cb0faac2a4c6a90bbfd..398ca59951557246319c514d6b093530cc14f92b 100644 (file)
@@ -39,6 +39,7 @@
 #include "output/spv/spv-legacy-data.h"
 #include "output/spv/spv-legacy-decoder.h"
 #include "output/spv/spv-light-decoder.h"
+#include "output/spv/spv-table-look.h"
 #include "output/spv/structure-xml-parser.h"
 
 #include "gl/c-ctype.h"
@@ -259,7 +260,7 @@ spv_item_destroy (struct spv_item *item)
       free (item->children);
 
       pivot_table_unref (item->table);
-      spv_legacy_properties_destroy (item->legacy_properties);
+      spv_table_look_destroy (item->table_look);
       free (item->bin_member);
       free (item->xml_member);
       free (item->subtype);
@@ -827,7 +828,7 @@ pivot_table_open_legacy (struct spv_item *item)
   error = spvxml_context_finish (&ctx, &v->node_);
 
   if (!error)
-    error = decode_spvdx_table (v, item->subtype, item->legacy_properties,
+    error = decode_spvdx_table (v, item->subtype, item->table_look,
                                 &data, &item->table);
 
   if (error)
@@ -905,8 +906,10 @@ spv_decode_container (const struct spvsx_container *c,
       if (ts->path)
         {
           item->xml_member = ts->path ? xstrdup (ts->path->text) : NULL;
-          char *error = decode_spvsx_legacy_properties (
-            table->table_properties, &item->legacy_properties);
+          char *error = (table->table_properties
+                         ? spv_table_look_decode (table->table_properties,
+                                                  &item->table_look)
+                         : xstrdup ("Legacy table lacks tableProperties"));
           if (error)
             {
               spv_item_destroy (item);
@@ -1184,6 +1187,21 @@ spv_close (struct spv_reader *spv)
     }
 }
 
+void
+spv_item_set_table_look (struct spv_item *item,
+                         const struct spv_table_look *look)
+{
+  /* If this is a table, install the table look in it.
+
+     (We can't just set item->table_look because light tables ignore it and
+     legacy tables sometimes override it.) */
+  if (spv_item_is_table (item))
+    spv_table_look_install (look, spv_item_get_table (item));
+
+  for (size_t i = 0; i < item->n_children; i++)
+    spv_item_set_table_look (item->children[i], look);
+}
+
 char * WARN_UNUSED_RESULT
 spv_decode_fmt_spec (uint32_t u32, struct fmt_spec *out)
 {
index 91321adc67485b778eb02f1293e3381b86d4171b..1753cdaabd3dd99c55a63b236649ee7e89af0863 100644 (file)
@@ -130,7 +130,7 @@ struct spv_item
 
     /* SPV_ITEM_TABLE only. */
     struct pivot_table *table;    /* NULL if not yet loaded. */
-    struct spv_legacy_properties *legacy_properties;
+    struct spv_table_look *table_look;
     char *bin_member;
     char *xml_member;
     char *subtype;
@@ -197,6 +197,9 @@ char *spv_item_get_legacy_data (const struct spv_item *, struct spv_data *)
 char *spv_item_get_legacy_table (const struct spv_item *, struct _xmlDoc **)
   WARN_UNUSED_RESULT;
 
+void spv_item_set_table_look (struct spv_item *,
+                              const struct spv_table_look *);
+
 char *spv_decode_fmt_spec (uint32_t u32, struct fmt_spec *) WARN_UNUSED_RESULT;
 
 #endif /* output/spv/spv.h */
index 61dc5d48f4b6d0837fe4df837e433f06bb6c8f07..67e83a1be1230ef272cd35de06a58cfc0e9b3453 100644 (file)
@@ -64,6 +64,8 @@ table
 => tableProperties? tableStructure
 
 tableProperties
+   :schemaLocation?            # Just for standalone .stt files.
+   :name?                      # Just for standalone .stt files.
 => generalProperties footnoteProperties cellFormatProperties borderProperties printingProperties
 
 generalProperties
index 4de1e294dbe0a3cd6a9ebb6fd4e114fe97f1f3b6..598b6f9ddc2b621c7e5e6ecfc861faa19b237764 100644 (file)
@@ -80,6 +80,9 @@ for details of the available output options.
 By default, if the source is corrupt or otherwise cannot be processed,
 the destination is not written.  These option make \fBpspp\-output\fR
 write the output as best it can, even with errors.
+.IP \fB\-\-table\-look=\fIfile\fR
+Reads a table style from \fIfile\fR and applies it to all of the
+output tables.  The file should a TableLook \fB.stt\fR file.
 .SS "Input Selection Options"
 The \fBdir\fR and \fBconvert\fR commands, by default, operate on all
 of the objects in the source SPV file, except for objects that are not
index 58547196bec6451e2b7a1f5b755d4ca1109887a5..8362ac57ff252ef78392c2c29fd33b809f52fbdd 100644 (file)
@@ -35,6 +35,7 @@
 #include "output/spv/spv-legacy-data.h"
 #include "output/spv/spv-output.h"
 #include "output/spv/spv-select.h"
+#include "output/spv/spv-table-look.h"
 #include "output/spv/spv.h"
 #include "output/table-item.h"
 #include "output/text-item.h"
@@ -75,6 +76,9 @@ static bool raw;
 /* -f, --force: Keep output file even on error. */
 static bool force;
 
+/* --table-look: TableLook to replace table style for conversion. */
+static struct spv_table_look *table_look;
+
 /* Number of warnings issued. */
 static size_t n_warnings;
 
@@ -278,6 +282,9 @@ run_convert (int argc UNUSED, char **argv)
   if (err)
     error (1, 0, "%s", err);
 
+  if (table_look)
+    spv_item_set_table_look (spv_get_root (spv), table_look);
+
   output_engine_push ();
   output_set_filename (argv[1]);
   string_map_replace (&output_options, "output-file", argv[2]);
@@ -746,6 +753,7 @@ main (int argc, char **argv)
 
   c->run (argc, argv);
 
+  spv_table_look_destroy (table_look);
   i18n_done ();
 
   return n_warnings ? EXIT_FAILURE : EXIT_SUCCESS;
@@ -872,6 +880,15 @@ parse_members (const char *arg)
   string_array_parse (&cm->members, ss_cstr (arg), ss_cstr (","));
 }
 
+static void
+parse_table_look (const char *arg)
+{
+  spv_table_look_destroy (table_look);
+  char *error_s = spv_table_look_read (arg, &table_look);
+  if (error_s)
+    error (1, 0, "%s", error_s);
+}
+
 static void
 parse_options (int argc, char *argv[])
 {
@@ -892,6 +909,7 @@ parse_options (int argc, char *argv[])
           OPT_OR,
           OPT_SORT,
           OPT_RAW,
+          OPT_TABLE_LOOK,
         };
       static const struct option long_options[] =
         {
@@ -912,6 +930,7 @@ parse_options (int argc, char *argv[])
 
           /* "convert" command options. */
           { "force", no_argument, NULL, 'f' },
+          { "table-look", required_argument, NULL, OPT_TABLE_LOOK },
 
           /* "dump-light-table" command options. */
           { "sort", no_argument, NULL, OPT_SORT },
@@ -987,6 +1006,10 @@ parse_options (int argc, char *argv[])
           raw = true;
           break;
 
+        case OPT_TABLE_LOOK:
+          parse_table_look (optarg);
+          break;
+
         case 'f':
           force = true;
           break;
@@ -1048,6 +1071,7 @@ The following options override \"convert\" behavior:\n\
   -O format=FORMAT          set destination format to FORMAT\n\
   -O OPTION=VALUE           set output option\n\
   -f, --force               keep output file even given errors\n\
+  --table-look=FILE         override tables' style with TableLook from FILE\n\
 Other options:\n\
   --help              display this help and exit\n\
   --version           output version information and exit\n",