pspp-output: New command get-table-look.
authorBen Pfaff <blp@cs.stanford.edu>
Thu, 29 Oct 2020 05:30:43 +0000 (22:30 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Thu, 29 Oct 2020 05:39:39 +0000 (22:39 -0700)
NEWS
doc/pspp-output.texi
src/output/pivot-table.h
src/output/spv/spv-table-look.c
src/output/spv/spv-table-look.h
utilities/pspp-output.1
utilities/pspp-output.c

diff --git a/NEWS b/NEWS
index a2f3a864ebd003f0d459ca6d2c5a7e9ac4c38ff4..694ec94f62d38f69757ba1696d424afa20102830 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -17,7 +17,11 @@ 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 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 new --table-look and --nth-commands options.
+ * New features in pspp-output:
+
+   - New --table-look and --nth-commands options.
+
+   - New get-table-look command.
 
 Changes from 1.4.0 to 1.4.1:
 
 
 Changes from 1.4.0 to 1.4.1:
 
index 35cc6004630e54a4139d74438ac2ddd972f79a72..bc68fa067fe1df65ad8759bb4ec7e0aca463751d 100644 (file)
@@ -29,6 +29,8 @@ SPSS 15 and earlier versions instead use @file{.spo} files.
 
 @t{pspp-output} [@var{options}] @t{convert} @var{source} @var{destination}
 
 
 @t{pspp-output} [@var{options}] @t{convert} @var{source} @var{destination}
 
+@t{pspp-output} [@var{options}] @t{get-table-look} @var{source} @var{destination}
+
 @t{pspp-output -@w{-}help}
 
 @t{pspp-output -@w{-}version}
 @t{pspp-output -@w{-}help}
 
 @t{pspp-output -@w{-}version}
@@ -42,6 +44,7 @@ developers may find useful for debugging.
 * The pspp-output detect Command::
 * The pspp-output dir Command::
 * The pspp-output convert Command::
 * The pspp-output detect Command::
 * The pspp-output dir Command::
 * The pspp-output convert Command::
+* The pspp-output get-table-look Command::
 * Input Selection Options::
 @end menu
 
 * Input Selection Options::
 @end menu
 
@@ -116,6 +119,23 @@ 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
 
 output tables.  The file should a TableLook @file{.stt} file.
 @end table
 
+@node The pspp-output get-table-look Command
+@section The @code{get-table-look} Command
+
+@display
+@t{pspp-output} [@var{options}] @t{get-table-look} @var{source} @var{destination}
+@end display
+
+Reads SPV file @var{source}, applies any selection options
+(@pxref{Input Selection Options}), picks the first table from the
+selected object, extracts the TableLook from that table, and writes it
+to @var{destination} (typically with an @file{.stt} extension) in the
+TableLook XML format.
+
+The user may use the TableLook file to change the style of tables in
+other files, by passing it to the @option{--table-look} option on the
+@code{convert} command.
+
 @node Input Selection Options
 @section Input Selection Options
 
 @node Input Selection Options
 @section Input Selection Options
 
index c4c9bb778f70626c37b1c03a7ed7db1fa474f346..ed8f9b7a2b33948cc961a3e1347a9d30d870f80f 100644 (file)
@@ -388,7 +388,7 @@ struct pivot_table
     bool show_caption;
     bool omit_empty;       /* Omit empty rows and columns? */
     size_t *current_layer; /* axis[PIVOT_AXIS_LAYER].n_dimensions elements. */
     bool show_caption;
     bool omit_empty;       /* Omit empty rows and columns? */
     size_t *current_layer; /* axis[PIVOT_AXIS_LAYER].n_dimensions elements. */
-    char *table_look;
+    char *table_look;      /* May be NULL. */
     enum settings_value_show show_values;
     enum settings_value_show show_variables;
     struct fmt_spec weight_format;
     enum settings_value_show show_values;
     enum settings_value_show show_variables;
     struct fmt_spec weight_format;
index d297c13981cbaa22bdb2ba4adf1d374a9ed00bb9..3a2fb4578c0734474b20599f1d8f0a4ef85e718f 100644 (file)
@@ -19,7 +19,9 @@
 #include "output/spv/spv-table-look.h"
 
 #include <errno.h>
 #include "output/spv/spv-table-look.h"
 
 #include <errno.h>
+#include <inttypes.h>
 #include <libxml/xmlreader.h>
 #include <libxml/xmlreader.h>
+#include <libxml/xmlwriter.h>
 #include <string.h>
 
 #include "output/spv/structure-xml-parser.h"
 #include <string.h>
 
 #include "output/spv/structure-xml-parser.h"
@@ -27,6 +29,9 @@
 #include "gl/read-file.h"
 #include "gl/xalloc.h"
 
 #include "gl/read-file.h"
 #include "gl/xalloc.h"
 
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
 static struct cell_color
 optional_color (int color, struct cell_color default_color)
 {
 static struct cell_color
 optional_color (int color, struct cell_color default_color)
 {
@@ -61,55 +66,55 @@ optional_pt (double inches, int default_pt)
   return inches != DBL_MAX ? inches * 72.0 + .5 : default_pt;
 }
 
   return inches != DBL_MAX ? inches * 72.0 + .5 : default_pt;
 }
 
+static const char *pivot_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",
+};
+
 static enum pivot_area
 pivot_area_from_name (const char *name)
 {
 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++)
   enum pivot_area area;
   for (area = 0; area < PIVOT_N_AREAS; area++)
-    if (!strcmp (name, area_names[area]))
+    if (!strcmp (name, pivot_area_names[area]))
       break;
   return area;
 }
 
       break;
   return area;
 }
 
+static const char *pivot_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",
+};
+
 static enum pivot_border
 pivot_border_from_name (const char *name)
 {
 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++)
   enum pivot_border border;
   for (border = 0; border < PIVOT_N_BORDERS; border++)
-    if (!strcmp (name, border_names[border]))
+    if (!strcmp (name, pivot_border_names[border]))
       break;
   return border;
 }
       break;
   return border;
 }
@@ -121,6 +126,8 @@ spv_table_look_decode (const struct spvsx_table_properties *in,
   struct spv_table_look *out = xzalloc (sizeof *out);
   char *error = NULL;
 
   struct spv_table_look *out = xzalloc (sizeof *out);
   char *error = NULL;
 
+  out->name = in->name ? xstrdup (in->name) : 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);
   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);
@@ -299,11 +306,210 @@ spv_table_look_read (const char *filename, struct spv_table_look **outp)
   return error;
 }
 
   return error;
 }
 
+static void
+write_attr (xmlTextWriter *xml, const char *name, const char *value)
+{
+  xmlTextWriterWriteAttribute (xml,
+                               CHAR_CAST (xmlChar *, name),
+                               CHAR_CAST (xmlChar *, value));
+}
+
+static void PRINTF_FORMAT (3, 4)
+write_attr_format (xmlTextWriter *xml, const char *name,
+                   const char *format, ...)
+{
+  va_list args;
+  va_start (args, format);
+  char *value = xvasprintf (format, args);
+  va_end (args);
+
+  write_attr (xml, name, value);
+  free (value);
+}
+
+static void
+write_attr_color (xmlTextWriter *xml, const char *name,
+                  const struct cell_color *color)
+{
+  write_attr_format (xml, name, "#%02"PRIx8"%02"PRIx8"%02"PRIx8,
+                     color->r, color->g, color->b);
+}
+
+static void
+write_attr_dimension (xmlTextWriter *xml, const char *name, int px)
+{
+  int pt = px / 96.0 * 72.0;
+  write_attr_format (xml, name, "%dpt", pt);
+}
+
+static void
+write_attr_bool (xmlTextWriter *xml, const char *name, bool b)
+{
+  write_attr (xml, name, b ? "true" : "false");
+}
+
+static void
+start_elem (xmlTextWriter *xml, const char *name)
+{
+  xmlTextWriterStartElement (xml, CHAR_CAST (xmlChar *, name));
+}
+
+static void
+end_elem (xmlTextWriter *xml)
+{
+  xmlTextWriterEndElement (xml);
+}
+
+char * WARN_UNUSED_RESULT
+spv_table_look_write (const char *filename, const struct spv_table_look *look)
+{
+  FILE *file = fopen (filename, "w");
+  if (!file)
+    return xasprintf (_("%s: create failed (%s)"), filename, strerror (errno));
+
+  xmlTextWriter *xml = xmlNewTextWriter (xmlOutputBufferCreateFile (
+                                           file, NULL));
+  if (!xml)
+    {
+      fclose (file);
+      return xasprintf (_("%s: failed to start writing XML"), filename);
+    }
+
+  xmlTextWriterSetIndent (xml, 1);
+  xmlTextWriterSetIndentString (xml, CHAR_CAST (xmlChar *, "    "));
+
+  xmlTextWriterStartDocument (xml, NULL, "UTF-8", NULL);
+  start_elem (xml, "tableProperties");
+  if (look->name)
+    write_attr (xml, "name", look->name);
+  write_attr (xml, "xmlns", "http://www.ibm.com/software/analytics/spss/xml/table-looks");
+  write_attr (xml, "xmlns:vizml", "http://www.ibm.com/software/analytics/spss/xml/visualization");
+  write_attr (xml, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
+  write_attr (xml, "xsi:schemaLocation", "http://www.ibm.com/software/analytics/spss/xml/table-looks http://www.ibm.com/software/analytics/spss/xml/table-looks/table-looks-1.4.xsd");
+
+  start_elem (xml, "generalProperties");
+  write_attr_bool (xml, "hideEmptyRows", look->omit_empty);
+  const int (*wr)[2] = look->width_ranges;
+  write_attr_format (xml, "maximumColumnWidth", "%d", wr[TABLE_HORZ][1]);
+  write_attr_format (xml, "maximumRowWidth", "%d", wr[TABLE_VERT][1]);
+  write_attr_format (xml, "minimumColumnWidth", "%d", wr[TABLE_HORZ][0]);
+  write_attr_format (xml, "minimumRowWidth", "%d", wr[TABLE_VERT][0]);
+  write_attr (xml, "rowDimensionLabels",
+              look->row_labels_in_corner ? "inCorner" : "nested");
+  end_elem (xml);
+
+  start_elem (xml, "footnoteProperties");
+  write_attr (xml, "markerPosition",
+              look->footnote_marker_superscripts ? "superscript" : "subscript");
+  write_attr (xml, "numberFormat",
+              look->show_numeric_markers ? "numeric" : "alphabetic");
+  end_elem (xml);
+
+  start_elem (xml, "cellFormatProperties");
+  for (enum pivot_area a = 0; a < PIVOT_N_AREAS; a++)
+    {
+      const struct area_style *area = &look->areas[a];
+      const struct font_style *font = &area->font_style;
+      const struct cell_style *cell = &area->cell_style;
+
+      start_elem (xml, pivot_area_names[a]);
+      if (a == PIVOT_AREA_DATA
+          && (!cell_color_equal (&font->fg[0], &font->fg[1])
+              || !cell_color_equal (&font->bg[0], &font->bg[1])))
+        {
+          write_attr_color (xml, "alternatingColor", &font->bg[1]);
+          write_attr_color (xml, "alternatingTextColor", &font->fg[1]);
+        }
+
+      start_elem (xml, "vizml:style");
+      write_attr_color (xml, "color", &font->fg[0]);
+      write_attr_color (xml, "color2", &font->bg[0]);
+      write_attr (xml, "font-family", font->typeface);
+      write_attr_format (xml, "font-size", "%dpt", font->size);
+      write_attr (xml, "font-weight", font->bold ? "bold" : "regular");
+      write_attr (xml, "font-underline",
+                  font->underline ? "underline" : "none");
+      write_attr (xml, "labelLocationVertical",
+                  cell->valign == TABLE_VALIGN_BOTTOM ? "negative"
+                  : cell->valign == TABLE_VALIGN_TOP ? "positive"
+                  : "center");
+      write_attr_dimension (xml, "margin-bottom", cell->margin[TABLE_VERT][1]);
+      write_attr_dimension (xml, "margin-left", cell->margin[TABLE_HORZ][0]);
+      write_attr_dimension (xml, "margin-right", cell->margin[TABLE_HORZ][1]);
+      write_attr_dimension (xml, "margin-top", cell->margin[TABLE_VERT][0]);
+      write_attr (xml, "textAlignment",
+                  cell->halign == TABLE_HALIGN_LEFT ? "left"
+                  : cell->halign == TABLE_HALIGN_RIGHT ? "right"
+                  : cell->halign == TABLE_HALIGN_CENTER ? "center"
+                  : cell->halign == TABLE_HALIGN_DECIMAL ? "decimal"
+                  : "mixed");
+      if (cell->halign == TABLE_HALIGN_DECIMAL)
+        write_attr_dimension (xml, "decimal-offset", cell->decimal_offset);
+      end_elem (xml);
+
+      end_elem (xml);
+    }
+  end_elem (xml);
+
+  start_elem (xml, "borderProperties");
+  for (enum pivot_border b = 0; b < PIVOT_N_BORDERS; b++)
+    {
+      const struct table_border_style *border = &look->borders[b];
+
+      start_elem (xml, pivot_border_names[b]);
+
+      static const char *table_stroke_names[TABLE_N_STROKES] =
+        {
+          [TABLE_STROKE_NONE] = "none",
+          [TABLE_STROKE_SOLID] = "solid",
+          [TABLE_STROKE_DASHED] = "dashed",
+          [TABLE_STROKE_THICK] = "thick",
+          [TABLE_STROKE_THIN] = "thin",
+          [TABLE_STROKE_DOUBLE] = "double",
+        };
+      write_attr (xml, "borderStyleType", table_stroke_names[border->stroke]);
+      write_attr_color (xml, "color", &border->color);
+      end_elem (xml);
+    }
+  end_elem (xml);
+
+  start_elem (xml, "printingProperties");
+  write_attr_bool (xml, "printAllLayers", look->print_all_layers);
+  write_attr_bool (xml, "rescaleLongTableToFitPage", look->shrink_to_length);
+  write_attr_bool (xml, "rescaleWideTableToFitPage", look->shrink_to_width);
+  write_attr_format (xml, "windowOrphanLines", "%zu", look->n_orphan_lines);
+  if (look->continuation && look->continuation[0]
+      && (look->top_continuation || look->bottom_continuation))
+    {
+      write_attr_format (xml, "continuationText", look->continuation);
+      write_attr_bool (xml, "continuationTextAtTop", look->top_continuation);
+      write_attr_bool (xml, "continuationTextAtBottom",
+                       look->bottom_continuation);
+    }
+  end_elem (xml);
+
+  xmlTextWriterEndDocument (xml);
+
+  xmlFreeTextWriter (xml);
+
+  fflush (file);
+  bool ok = !ferror (file);
+  if (fclose (file) == EOF)
+    ok = false;
+
+  if (!ok)
+    return xasprintf (_("%s: error writing file (%s)"),
+                      filename, strerror (errno));
+
+  return NULL;
+}
+
 void
 spv_table_look_destroy (struct spv_table_look *look)
 {
   if (look)
     {
 void
 spv_table_look_destroy (struct spv_table_look *look)
 {
   if (look)
     {
+      free (look->name);
       for (size_t i = 0; i < PIVOT_N_AREAS; i++)
         area_style_uninit (&look->areas[i]);
       free (look->continuation);
       for (size_t i = 0; i < PIVOT_N_AREAS; i++)
         area_style_uninit (&look->areas[i]);
       free (look->continuation);
@@ -315,6 +521,10 @@ void
 spv_table_look_install (const struct spv_table_look *look,
                         struct pivot_table *table)
 {
 spv_table_look_install (const struct spv_table_look *look,
                         struct pivot_table *table)
 {
+  free (table->table_look);
+  if (look->name)
+    table->table_look = xstrdup (look->name);
+
   table->omit_empty = look->omit_empty;
 
   for (enum table_axis axis = 0; axis < TABLE_N_AXES; axis++)
   table->omit_empty = look->omit_empty;
 
   for (enum table_axis axis = 0; axis < TABLE_N_AXES; axis++)
@@ -343,3 +553,37 @@ spv_table_look_install (const struct spv_table_look *look,
   table->continuation = xstrdup (look->continuation);
   table->n_orphan_lines = look->n_orphan_lines;
 }
   table->continuation = xstrdup (look->continuation);
   table->n_orphan_lines = look->n_orphan_lines;
 }
+
+struct spv_table_look *
+spv_table_look_get (const struct pivot_table *table)
+{
+  struct spv_table_look *look = xzalloc (sizeof *look);
+
+  look->name = table->table_look ? xstrdup (table->table_look) : NULL;
+
+  look->omit_empty = table->omit_empty;
+
+  for (enum table_axis axis = 0; axis < TABLE_N_AXES; axis++)
+    for (int i = 0; i < 2; i++)
+      look->width_ranges[axis][i] = table->sizing[axis].range[i];
+  look->row_labels_in_corner = table->row_labels_in_corner;
+
+  look->footnote_marker_superscripts = table->footnote_marker_superscripts;
+  look->show_numeric_markers = table->show_numeric_markers;
+
+  for (size_t i = 0; i < PIVOT_N_AREAS; i++)
+    area_style_copy (NULL, &look->areas[i], &table->areas[i]);
+  for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
+    look->borders[i] = table->borders[i];
+
+  look->print_all_layers = table->print_all_layers;
+  look->paginate_layers = table->paginate_layers;
+  look->shrink_to_width = table->shrink_to_fit[TABLE_HORZ];
+  look->shrink_to_length = table->shrink_to_fit[TABLE_VERT];
+  look->top_continuation = table->top_continuation;
+  look->bottom_continuation = table->bottom_continuation;
+  look->continuation = xstrdup (table->continuation);
+  look->n_orphan_lines = table->n_orphan_lines;
+
+  return look;
+}
index 3e817571ddb4d678c143ea800e010d78f4f1073e..98675950215d7d4374d21247dec712fa54ea0140 100644 (file)
@@ -33,6 +33,8 @@ struct spvsx_table_properties;
 
 struct spv_table_look
   {
 
 struct spv_table_look
   {
+    char *name;                 /* May be null. */
+
     /* General properties. */
     bool omit_empty;
     int width_ranges[TABLE_N_AXES][2];      /* In 1/96" units. */
     /* General properties. */
     bool omit_empty;
     int width_ranges[TABLE_N_AXES][2];      /* In 1/96" units. */
@@ -63,8 +65,11 @@ char *spv_table_look_decode (const struct spvsx_table_properties *,
   WARN_UNUSED_RESULT;
 char *spv_table_look_read (const char *, struct spv_table_look **)
   WARN_UNUSED_RESULT;
   WARN_UNUSED_RESULT;
 char *spv_table_look_read (const char *, struct spv_table_look **)
   WARN_UNUSED_RESULT;
+char *spv_table_look_write (const char *, const struct spv_table_look *)
+  WARN_UNUSED_RESULT;
 
 void spv_table_look_install (const struct spv_table_look *,
                              struct pivot_table *);
 
 void spv_table_look_install (const struct spv_table_look *,
                              struct pivot_table *);
+struct spv_table_look *spv_table_look_get (const struct pivot_table *);
 
 #endif /* output/spv/spv-table-look.h */
 
 #endif /* output/spv/spv-table-look.h */
index 598b6f9ddc2b621c7e5e6ecfc861faa19b237764..252fec9a005a9edbf4634a7272088896a310b939 100644 (file)
@@ -16,6 +16,8 @@ pspp\-output \- convert and operate on SPSS viewer (SPV) files
 .br
 \fBpspp\-output \fR[\fIoptions\fR] \fBconvert\fR \fIsource destination\fR
 .br
 .br
 \fBpspp\-output \fR[\fIoptions\fR] \fBconvert\fR \fIsource destination\fR
 .br
+\fBpspp\-output \fR[\fIoptions\fR] \fBget\-table\-look\fR \fIsource destination\fR
+.br
 \fBpspp\-output \-\-help\fR | \fB\-h\fR
 .br
 \fBpspp\-output \-\-version\fR | \fB\-v\fR
 \fBpspp\-output \-\-help\fR | \fB\-h\fR
 .br
 \fBpspp\-output \-\-version\fR | \fB\-v\fR
@@ -83,6 +85,18 @@ 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.
 .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 The \fBget\-table\-look\fR command
+When invoked as \fBpspp\-output get\-table\-look \fIsource
+destination\fR, \fBpspp\-output\fR reads SPV file \fIsource\fR,
+applies any selection options (as described under \fBInput Selection
+Options\fR below), picks the first table from the selected object,
+extracts the TableLook from that table, and writes it to
+\fIdestination\fR (typically with an \fB.stt\fR extension) in the
+TableLook XML format.
+.PP
+The user may use the TableLook file to change the style of tables in
+other files, by passing it to the \fB\-\-table\-look\fR option on the
+\fBconvert\fR command.
 .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
 .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 8362ac57ff252ef78392c2c29fd33b809f52fbdd..4b9710a020bc1c3c24ddb7a08e22f8e60c511fb3 100644 (file)
@@ -325,6 +325,45 @@ run_convert (int argc UNUSED, char **argv)
     }
 }
 
     }
 }
 
+static const struct pivot_table *
+get_first_table (const struct spv_reader *spv)
+{
+  struct spv_item **items;
+  size_t n_items;
+  spv_select (spv, criteria, n_criteria, &items, &n_items);
+
+  for (size_t i = 0; i < n_items; i++)
+    if (spv_item_is_table (items[i]))
+      {
+        free (items);
+        return spv_item_get_table (items[i]);
+      }
+
+  free (items);
+  return NULL;
+}
+
+static void
+run_get_table_look (int argc UNUSED, char **argv)
+{
+  struct spv_reader *spv;
+  char *err = spv_open (argv[1], &spv);
+  if (err)
+    error (1, 0, "%s", err);
+
+  const struct pivot_table *table = get_first_table (spv);
+  if (!table)
+    error (1, 0, "%s: no tables found", argv[1]);
+
+  struct spv_table_look *look = spv_table_look_get (table);
+  err = spv_table_look_write (argv[2], look);
+  if (err)
+    error (1, 0, "%s", err);
+  spv_table_look_destroy (look);
+
+  spv_close (spv);
+}
+
 static void
 run_dump (int argc UNUSED, char **argv)
 {
 static void
 run_dump (int argc UNUSED, char **argv)
 {
@@ -672,6 +711,7 @@ static const struct command commands[] =
     { "detect", 1, 1, run_detect },
     { "dir", 1, 1, run_directory },
     { "convert", 2, 2, run_convert },
     { "detect", 1, 1, run_detect },
     { "dir", 1, 1, run_directory },
     { "convert", 2, 2, run_convert },
+    { "get-table-look", 2, 2, run_get_table_look },
 
     /* Undocumented commands. */
     { "dump", 1, 1, run_dump },
 
     /* Undocumented commands. */
     { "dump", 1, 1, run_dump },
@@ -1053,6 +1093,7 @@ The following commands are available:\n\
   detect FILE            Detect whether FILE is an SPV file.\n\
   dir FILE               List tables and other items in FILE.\n\
   convert SOURCE DEST    Convert .spv SOURCE to DEST.\n\
   detect FILE            Detect whether FILE is an SPV file.\n\
   dir FILE               List tables and other items in FILE.\n\
   convert SOURCE DEST    Convert .spv SOURCE to DEST.\n\
+  get-table-look SOURCE DEST  Copies first selected TableLook into DEST\n\
 \n\
 Input selection options for \"dir\" and \"convert\":\n\
   --select=CLASS...   include only some kinds of objects\n\
 \n\
 Input selection options for \"dir\" and \"convert\":\n\
   --select=CLASS...   include only some kinds of objects\n\