From a22f79456929fc7edc9d65234ab05f4966c86679 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Mon, 26 Oct 2020 19:31:18 -0700 Subject: [PATCH] pspp-output: New option --table-look. --- NEWS | 2 +- doc/pspp-output.texi | 4 + src/output/spv/automake.mk | 2 + src/output/spv/spv-legacy-decoder.c | 293 +---------------------- src/output/spv/spv-legacy-decoder.h | 14 +- src/output/spv/spv-select.c | 4 + src/output/spv/spv-table-look.c | 343 +++++++++++++++++++++++++++ src/output/spv/spv-table-look.h | 70 ++++++ src/output/spv/spv.c | 26 +- src/output/spv/spv.h | 5 +- src/output/spv/structure-xml.grammar | 2 + utilities/pspp-output.1 | 3 + utilities/pspp-output.c | 24 ++ 13 files changed, 485 insertions(+), 307 deletions(-) create mode 100644 src/output/spv/spv-table-look.c create mode 100644 src/output/spv/spv-table-look.h diff --git a/NEWS b/NEWS index 00b3c38e8c..a2f3a864eb 100644 --- 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: diff --git a/doc/pspp-output.texi b/doc/pspp-output.texi index 29c625e0cd..35cc600463 100644 --- a/doc/pspp-output.texi +++ b/doc/pspp-output.texi @@ -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 diff --git a/src/output/spv/automake.mk b/src/output/spv/automake.mk index 37f98c3f13..970c821318 100644 --- a/src/output/spv/automake.mk +++ b/src/output/spv/automake.mk @@ -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 \ diff --git a/src/output/spv/spv-legacy-decoder.c b/src/output/spv/spv-legacy-decoder.c index 211813ac1b..b5f4e9eeac 100644 --- a/src/output/spv/spv-legacy-decoder.c +++ b/src/output/spv/spv-legacy-decoder.c @@ -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" @@ -48,31 +49,6 @@ #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; diff --git a/src/output/spv/spv-legacy-decoder.h b/src/output/spv/spv-legacy-decoder.h index 9ff2428b47..2cf9d6b10f 100644 --- a/src/output/spv/spv-legacy-decoder.h +++ b/src/output/spv/spv-legacy-decoder.h @@ -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. */ @@ -25,20 +25,12 @@ 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; diff --git a/src/output/spv/spv-select.c b/src/output/spv/spv-select.c index 6be9742795..21b560d720 100644 --- a/src/output/spv/spv-select.c +++ b/src/output/spv/spv-select.c @@ -27,6 +27,10 @@ #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 index 0000000000..178c8ae5bf --- /dev/null +++ b/src/output/spv/spv-table-look.c @@ -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 . */ + +#include + +#include "output/spv/spv-table-look.h" + +#include +#include +#include + +#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 index 0000000000..3e817571dd --- /dev/null +++ b/src/output/spv/spv-table-look.h @@ -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 . */ + +#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 +#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 */ diff --git a/src/output/spv/spv.c b/src/output/spv/spv.c index 25fb962aae..398ca59951 100644 --- a/src/output/spv/spv.c +++ b/src/output/spv/spv.c @@ -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) { diff --git a/src/output/spv/spv.h b/src/output/spv/spv.h index 91321adc67..1753cdaabd 100644 --- a/src/output/spv/spv.h +++ b/src/output/spv/spv.h @@ -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 */ diff --git a/src/output/spv/structure-xml.grammar b/src/output/spv/structure-xml.grammar index 61dc5d48f4..67e83a1be1 100644 --- a/src/output/spv/structure-xml.grammar +++ b/src/output/spv/structure-xml.grammar @@ -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 diff --git a/utilities/pspp-output.1 b/utilities/pspp-output.1 index 4de1e294db..598b6f9ddc 100644 --- a/utilities/pspp-output.1 +++ b/utilities/pspp-output.1 @@ -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 diff --git a/utilities/pspp-output.c b/utilities/pspp-output.c index 58547196be..8362ac57ff 100644 --- a/utilities/pspp-output.c +++ b/utilities/pspp-output.c @@ -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", -- 2.30.2