From 6b40f1315cf46ca2417c10ce08bcf62941bdd305 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Mon, 13 Aug 2007 00:26:31 +0000 Subject: [PATCH] Add support for charts to ASCII driver. Bug #16364. Thanks to John Darrington for review. * ascii.c (struct ascii_driver_ext): Add chart_type, chart_file_name, chart_cnt members. (ascii_open_driver): Initialize new members. (static array option_tab): Add new options, reorganize slightly. (handle_option): Handle new options. (ascii_submit): New function. (ascii_chart_initialise): Implement. (ascii_chart_finalise): Implement. * chart.c (chart_init_separate): New function. (chart_finalise_separate): New function. * dummy-chart.c (chart_init_separate): New function. (chart_finalise_separate): New function. * html.c (html_open_driver): Don't free chart_file_name. (html_close_driver): Do free chart_file_name. (handle_option): Only give an error for chart-files options that don't contain "#". (html_initialise_chart): Use new chart_init_separate. (html_finalise_chart): Use new chart_finalise_separate. --- doc/configuring.texi | 15 ++++++ src/output/ChangeLog | 27 ++++++++++ src/output/ascii.c | 112 ++++++++++++++++++++++++++++++++------- src/output/chart.c | 57 ++++++++++++++++---- src/output/chart.h | 6 +++ src/output/dummy-chart.c | 10 ++++ src/output/html.c | 43 +++++---------- 7 files changed, 211 insertions(+), 59 deletions(-) diff --git a/doc/configuring.texi b/doc/configuring.texi index 43be3d67..c299d9c8 100644 --- a/doc/configuring.texi +++ b/doc/configuring.texi @@ -671,6 +671,21 @@ File to which output should be sent. This can be an ordinary file name (e.g., @code{"pspp.txt"}), a pipe (e.g., @code{"|more"}), or stdout (@code{"-"}). Default: @code{"pspp.list"}. +@item chart-files=@var{file-name-template} +Template for the file names used for charts. The name should contain +a single @samp{#}, which is replaced by the chart number. Default: +@file{"pspp-#.png"}. + +@item chart-type=@var{type}. +Type of charts to output. Available types typically include @samp{X}, +@samp{png}, @samp{gif}, @samp{svg}, @samp{ps}, @samp{cgm}, @samp{fig}, +@samp{pcl}, @samp{hpgl}, @samp{regis}, @samp{tek}, and @samp{meta}. +Default: @samp{png}. + +You may specify @samp{none} to disable chart output. Charts are also +disabled if your installation of PSPP was compiled without +@code{libplot}. + @item paginate=@var{boolean} If set, a formfeed will be written at the end of every page. Default: diff --git a/src/output/ChangeLog b/src/output/ChangeLog index 12f93d64..27f8ade3 100644 --- a/src/output/ChangeLog +++ b/src/output/ChangeLog @@ -1,3 +1,30 @@ +2007-08-12 Ben Pfaff + + Add support for charts to ASCII driver. Bug #16364. + Thanks to John Darrington for review. + + * ascii.c (struct ascii_driver_ext): Add chart_type, + chart_file_name, chart_cnt members. + (ascii_open_driver): Initialize new members. + (static array option_tab): Add new options, reorganize slightly. + (handle_option): Handle new options. + (ascii_submit): New function. + (ascii_chart_initialise): Implement. + (ascii_chart_finalise): Implement. + + * chart.c (chart_init_separate): New function. + (chart_finalise_separate): New function. + + * dummy-chart.c (chart_init_separate): New function. + (chart_finalise_separate): New function. + + * html.c (html_open_driver): Don't free chart_file_name. + (html_close_driver): Do free chart_file_name. + (handle_option): Only give an error for chart-files options that + don't contain "#". + (html_initialise_chart): Use new chart_init_separate. + (html_finalise_chart): Use new chart_finalise_separate. + 2007-07-25 Ben Pfaff Allow the user to specify an initialization string to write at the diff --git a/src/output/ascii.c b/src/output/ascii.c index 0a26c75a..2278f809 100644 --- a/src/output/ascii.c +++ b/src/output/ascii.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -40,6 +41,9 @@ /* ASCII driver options: (defaults listed first) output-file="pspp.list" + chart-files="pspp-#.png" Name used for charts. + chart-type=png Format of charts (use "none" to disable). + paginate=on|off Formfeeds are desired? tab-width=8 Width of a tab; 0 to not use tabs. @@ -101,6 +105,8 @@ struct ascii_driver_ext bool squeeze_blank_lines; /* Squeeze multiple blank lines into one? */ enum emphasis_style emphasis; /* How to emphasize text. */ int tab_width; /* Width of a tab; 0 not to use tabs. */ + const char *chart_type; /* Type of charts to output; NULL for none. */ + const char *chart_file_name; /* Name of files used for charts. */ int page_length; /* Page length before subtracting margins. */ int top_margin; /* Top margin in lines. */ @@ -115,6 +121,7 @@ struct ascii_driver_ext int page_number; /* Current page number. */ struct line *lines; /* Page content. */ int line_cap; /* Number of lines allocated. */ + int chart_cnt; /* Number of charts so far. */ }; static void ascii_flush (struct outp_driver *); @@ -141,6 +148,8 @@ ascii_open_driver (struct outp_driver *this, struct substring options) x->squeeze_blank_lines = false; x->emphasis = EMPH_BOLD; x->tab_width = 8; + x->chart_file_name = pool_strdup (x->pool, "pspp-#.png"); + x->chart_type = pool_strdup (x->pool, "png"); x->page_length = 66; x->top_margin = 2; x->bottom_margin = 2; @@ -152,6 +161,7 @@ ascii_open_driver (struct outp_driver *this, struct substring options) x->page_number = 0; x->lines = NULL; x->line_cap = 0; + x->chart_cnt = 0; if (!outp_parse_options (options, handle_option, this)) goto error; @@ -237,7 +247,6 @@ enum emphasis_arg, nonneg_int_arg, pos_int_arg, - output_file_arg, string_arg }; @@ -249,8 +258,6 @@ static const struct outp_option option_tab[] = {"emphasis", emphasis_arg, 0}, - {"output-file", output_file_arg, 0}, - {"length", pos_int_arg, 0}, {"width", pos_int_arg, 1}, @@ -258,7 +265,10 @@ static const struct outp_option option_tab[] = {"bottom-margin", nonneg_int_arg, 1}, {"tab-width", nonneg_int_arg, 2}, - {"init", string_arg, 0}, + {"output-file", string_arg, 0}, + {"chart-files", string_arg, 1}, + {"chart-type", string_arg, 2}, + {"init", string_arg, 3}, {NULL, 0, 0}, }; @@ -295,9 +305,6 @@ handle_option (struct outp_driver *this, const char *key, case -1: error (0, 0, _("ascii: unknown parameter `%s'"), key); break; - case output_file_arg: - x->file_name = pool_strdup (x->pool, value); - break; case pos_int_arg: { char *tail; @@ -397,8 +404,27 @@ handle_option (struct outp_driver *this, const char *key, } break; case string_arg: - free (x->init); - x->init = pool_strdup (x->pool, value); + switch (subcat) + { + case 0: + x->file_name = pool_strdup (x->pool, value); + break; + case 1: + if (ds_find_char (val, '#') != SIZE_MAX) + x->chart_file_name = pool_strdup (x->pool, value); + else + error (0, 0, _("`chart-files' value must contain `#'")); + break; + case 2: + if (value[0] != '\0') + x->chart_type = pool_strdup (x->pool, value); + else + x->chart_type = NULL; + break; + case 3: + x->init = pool_strdup (x->pool, value); + break; + } break; default: NOT_REACHED (); @@ -504,6 +530,15 @@ ascii_line (struct outp_driver *this, } } +static void +ascii_submit (struct outp_driver *this UNUSED, struct som_entity *s) +{ + extern struct som_table_class tab_table_class; + + assert (s->class == &tab_table_class); + assert (s->type == SOM_CHART); +} + static void text_draw (struct outp_driver *this, enum outp_font font, @@ -543,9 +578,10 @@ text_draw (struct outp_driver *this, ext->lines[y].chars[x++] = *string++ | attr; } -/* Divides the text T->S into lines of width T->H. Sets T->V to the - number of lines necessary. Actually draws the text if DRAW is - true. */ +/* Divides the text T->S into lines of width T->H. Sets *WIDTH + to the maximum width of a line and *HEIGHT to the number of + lines, if those arguments are non-null. Actually draws the + text if DRAW is true. */ static void delineate (struct outp_driver *this, const struct outp_text *text, bool draw, int *width, int *height) @@ -623,7 +659,6 @@ ascii_text_draw (struct outp_driver *this, const struct outp_text *t) assert (this->page_open); delineate (this, t, true, NULL, NULL); } - /* ascii_close_page () and support routines. */ @@ -759,16 +794,55 @@ ascii_flush (struct outp_driver *this) } static void -ascii_chart_initialise (struct outp_driver *d UNUSED, struct chart *ch) +ascii_chart_initialise (struct outp_driver *this, struct chart *ch) { - error (0, 0, _("ascii: charts are unsupported by this driver")); - ch->lp = 0; + struct ascii_driver_ext *x = this->ext; + struct outp_text t; + char *text; + + if (x->chart_type == NULL) + return; + + /* Initialize chart. */ + chart_init_separate (ch, x->chart_type, x->chart_file_name, ++x->chart_cnt); + if (ch->file_name == NULL) + return; + + /* Mention chart in output. + First advance current position. */ + if (!this->page_open) + outp_open_page (this); + else + { + this->cp_y++; + if (this->cp_y >= this->length) + { + outp_close_page (this); + outp_open_page (this); + } + } + + /* Then write the text. */ + text = xasprintf ("See %s for a chart.", ch->file_name); + t.font = OUTP_FIXED; + t.justification = OUTP_LEFT; + t.string = ss_cstr (text); + t.h = this->width; + t.v = 1; + t.x = 0; + t.y = this->cp_y; + ascii_text_draw (this, &t); + this->cp_y++; + + free (text); } static void -ascii_chart_finalise (struct outp_driver *d UNUSED, struct chart *ch UNUSED) +ascii_chart_finalise (struct outp_driver *this, struct chart *ch) { - + struct ascii_driver_ext *x = this->ext; + if (x->chart_type != NULL) + chart_finalise_separate (ch); } const struct outp_class ascii_class = @@ -783,7 +857,7 @@ const struct outp_class ascii_class = ascii_close_page, ascii_flush, - NULL, + ascii_submit, ascii_line, ascii_text_metrics, diff --git a/src/output/chart.c b/src/output/chart.c index a7086298..9ae12f9c 100644 --- a/src/output/chart.c +++ b/src/output/chart.c @@ -15,20 +15,29 @@ along with this program. If not, see . */ #include + +#include + +#include +#include +#include +#include +#include #include #include -#include -#include #include -#include -#include -#include -#include "chart.h" -#include +#include + #include -#include "manager.h" -#include "output.h" +#include +#include +#include + +#include "error.h" + +#include "gettext.h" +#define _(msgid) gettext (msgid) extern struct som_table_class tab_table_class; @@ -117,3 +126,33 @@ chart_submit(struct chart *chart) free(chart); } +void +chart_init_separate (struct chart *ch, const char *type, + const char *file_name_tmpl, int number) +{ + FILE *fp; + int number_pos; + + number_pos = strchr (file_name_tmpl, '#') - file_name_tmpl; + ch->file_name = xasprintf ("%.*s%d%s", + number_pos, file_name_tmpl, + number, + file_name_tmpl + number_pos + 1); + fp = fopen (ch->file_name, "wb"); + if (fp == NULL) + { + error (0, errno, _("creating \"%s\""), ch->file_name); + free (ch->file_name); + ch->file_name = NULL; + return; + } + + ch->pl_params = pl_newplparams (); + ch->lp = pl_newpl_r (type, 0, fp, stderr, ch->pl_params); +} + +void +chart_finalise_separate (struct chart *ch) +{ + free (ch->file_name); +} diff --git a/src/output/chart.h b/src/output/chart.h index d931e175..43543cf5 100644 --- a/src/output/chart.h +++ b/src/output/chart.h @@ -85,4 +85,10 @@ struct chart { struct chart * chart_create(void); void chart_submit(struct chart *ch); +/* Helper functions for output drivers that put each chart into a + separate file. */ +void chart_init_separate (struct chart *, const char *type, + const char *file_name_tmpl, int number); +void chart_finalise_separate (struct chart *); + #endif diff --git a/src/output/dummy-chart.c b/src/output/dummy-chart.c index 622107a2..8850a092 100644 --- a/src/output/dummy-chart.c +++ b/src/output/dummy-chart.c @@ -31,3 +31,13 @@ chart_submit(struct chart *chart UNUSED) { } +void +chart_init_separate (struct chart *ch UNUSED, const char *type UNUSED, + const *file_name_tmpl UNUSED, int number UNUSED) +{ +} + +void +chart_finalise_separate (struct chart *ch) +{ +} diff --git a/src/output/html.c b/src/output/html.c index b938333f..76e152ae 100644 --- a/src/output/html.c +++ b/src/output/html.c @@ -18,6 +18,7 @@ #include "chart.h" #include "htmlP.h" #include +#include #include #include #include @@ -34,11 +35,15 @@ #include "table.h" #include -#include "size_max.h" - #include "gettext.h" #define _(msgid) gettext (msgid) +/* HTML driver options: (defaults listed first) + + output-file="pspp.html" + chart-files="pspp-#.png" +*/ + static void escape_string (FILE *file, const char *text, size_t length, const char *space); @@ -83,7 +88,6 @@ html_open_driver (struct outp_driver *this, struct substring options) fputs (" LINK=\"#1f00ff\" ALINK=\"#ff0000\" VLINK=\"#9900dd\">\n", x->file); print_title_tag (x->file, "H1", outp_title); print_title_tag (x->file, "H2", outp_subtitle); - free (x->chart_file_name); return true; @@ -123,6 +127,7 @@ html_close_driver (struct outp_driver *this) } else ok = true; + free (x->chart_file_name); free (x->file_name); free (x); @@ -179,7 +184,8 @@ handle_option (struct outp_driver *this, free (x->chart_file_name); x->chart_file_name = ds_xstrdup (val); } - error (0, 0, _("`chart-files' value must contain `#'")); + else + error (0, 0, _("`chart-files' value must contain `#'")); break; default: NOT_REACHED (); @@ -359,39 +365,14 @@ output_tab_table (struct outp_driver *this, struct tab_table *t) static void html_initialise_chart (struct outp_driver *this UNUSED, struct chart *ch) { -#ifdef NO_CHARTS - ch->lp = NULL; -#else struct html_driver_ext *x = this->ext; - - FILE *fp; - int number_pos; - - x->chart_cnt++; - - number_pos = strchr (x->chart_file_name, '#') - x->chart_file_name; - ch->file_name = xasprintf ("%.*s%d%s", - number_pos, x->chart_file_name, - (int) x->chart_cnt, - x->chart_file_name + number_pos + 1); - fp = fopen (ch->file_name, "wb"); - if (fp == NULL) - { - error (0, errno, _("creating \"%s\""), ch->file_name); - free (ch->file_name); - ch->file_name = NULL; - return; - } - - ch->pl_params = pl_newplparams (); - ch->lp = pl_newpl_r ("png", 0, fp, stderr, ch->pl_params); -#endif + chart_init_separate (ch, "png", x->chart_file_name, ++x->chart_cnt); } static void html_finalise_chart(struct outp_driver *d UNUSED, struct chart *ch) { - free(ch->file_name); + chart_finalise_separate (ch); } -- 2.30.2