X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;ds=sidebyside;f=src%2Foutput%2Fhtml.c;h=f8ce21ce5dc97bdb49c2680c23c36287cf8c195b;hb=6fa20f2f419eac61340d9b93d5cdde01c281c9ec;hp=69222f5e229b60120ca49c4c1a71a39428b8c9b8;hpb=a269ac443ece11402969c661efae4c13cbbdea0a;p=pspp diff --git a/src/output/html.c b/src/output/html.c index 69222f5e22..f8ce21ce5d 100644 --- a/src/output/html.c +++ b/src/output/html.c @@ -1,644 +1,679 @@ -/* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. - Written by Ben Pfaff . +/* PSPP - a program for statistical analysis. + Copyright (C) 1997-9, 2000, 2009, 2010, 2011, 2012, 2013, 2014, 2017, + 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 2 of the - License, or (at your option) any later version. + 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. + 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with this program. If not, see . */ #include -#include "chart.h" -#include "htmlP.h" -#include + #include +#include #include #include #include #include - -#include -#include -#include -#include -#include "getline.h" -#include "getlogin_r.h" -#include "output.h" -#include "manager.h" -#include "table.h" -#include -#include +#include + +#include "data/file-name.h" +#include "data/file-handle-def.h" +#include "libpspp/assertion.h" +#include "libpspp/cast.h" +#include "libpspp/compiler.h" +#include "libpspp/i18n.h" +#include "libpspp/message.h" +#include "libpspp/version.h" +#include "output/cairo-chart.h" +#include "output/chart-item.h" +#include "output/driver-provider.h" +#include "output/message-item.h" +#include "output/options.h" +#include "output/output-item-provider.h" +#include "output/table-provider.h" +#include "output/table-item.h" +#include "output/text-item.h" + +#include "gl/minmax.h" +#include "gl/xalloc.h" #include "gettext.h" #define _(msgid) gettext (msgid) -/* Prototypes. */ -static int postopen (struct file_ext *); -static int preclose (struct file_ext *); +struct html_driver + { + struct output_driver driver; +#ifdef HAVE_CAIRO + struct cell_color fg; + struct cell_color bg; +#endif + struct file_handle *handle; + char *chart_file_name; + + FILE *file; + size_t chart_cnt; + + bool bare; + bool css; + bool borders; + }; + +static const struct output_driver_class html_driver_class; -static int -html_open_global (struct outp_class *this UNUSED) +static void html_output_table (struct html_driver *, const struct table_item *); +static void escape_string (FILE *file, const char *text, + const char *space, const char *newline); +static void print_title_tag (FILE *file, const char *name, + const char *content); + +static struct html_driver * +html_driver_cast (struct output_driver *driver) { - return 1; + assert (driver->class == &html_driver_class); + return UP_CAST (driver, struct html_driver, driver); } -static int -html_close_global (struct outp_class *this UNUSED) +static struct driver_option * +opt (struct output_driver *d, struct string_map *options, const char *key, + const char *default_value) { - return 1; + return driver_option_get (d, options, key, default_value); } -static int -html_preopen_driver (struct outp_driver *this) +static void +put_header (struct html_driver *html) { - struct html_driver_ext *x; - - assert (this->driver_open == 0); - msg (VM (1), _("HTML driver initializing as `%s'..."), this->name); - - this->ext = x = xmalloc (sizeof *x); - this->res = 0; - this->horiz = this->vert = 0; - this->width = this->length = 0; - - this->cp_x = this->cp_y = 0; - - x->prologue_fn = NULL; - - x->file.filename = NULL; - x->file.mode = "w"; - x->file.file = NULL; - x->file.sequence_no = &x->sequence_no; - x->file.param = this; - x->file.postopen = postopen; - x->file.preclose = preclose; - - x->sequence_no = 0; - - return 1; + fputs ("\n", html->file); + fprintf (html->file, "file, " lang=\"%s\"", ln); + free (ln); + fprintf (html->file, ">\n"); + fputs ("\n", html->file); + print_title_tag (html->file, "title", _("PSPP Output")); + fprintf (html->file, "\n", version); + fputs ("\n", html->file); + + if (html->css) + { + fputs ("\n", + html->file); + } + fputs ("\n", html->file); + fputs ("\n", html->file); } -static int -html_postopen_driver (struct outp_driver *this) +static struct output_driver * +html_create (struct file_handle *fh, enum settings_output_devices device_type, + struct string_map *o) { - struct html_driver_ext *x = this->ext; + struct output_driver *d; + struct html_driver *html; + + html = xzalloc (sizeof *html); + d = &html->driver; + output_driver_init (&html->driver, &html_driver_class, fh_get_file_name (fh), + device_type); + html->bare = parse_boolean (opt (d, o, "bare", "false")); + html->css = parse_boolean (opt (d, o, "css", "true")); + html->borders = parse_boolean (opt (d, o, "borders", "true")); + + html->handle = fh; + html->chart_file_name = parse_chart_file_name (opt (d, o, "charts", + fh_get_file_name (fh))); + html->file = NULL; + html->chart_cnt = 1; +#ifdef HAVE_CAIRO + html->bg = parse_color (opt (d, o, "background-color", "#FFFFFFFFFFFF")); + html->fg = parse_color (opt (d, o, "foreground-color", "#000000000000")); +#endif + html->file = fn_open (html->handle, "w"); + if (html->file == NULL) + { + msg_error (errno, _("error opening output file `%s'"), fh_get_file_name (html->handle)); + goto error; + } - assert (this->driver_open == 0); - if (NULL == x->file.filename) - x->file.filename = xstrdup ("pspp.html"); - - if (x->prologue_fn == NULL) - x->prologue_fn = xstrdup ("html-prologue"); + if (!html->bare) + put_header (html); - msg (VM (2), _("%s: Initialization complete."), this->name); - this->driver_open = 1; + return d; - return 1; + error: + output_driver_destroy (d); + return NULL; } -static int -html_close_driver (struct outp_driver *this) +/* Emits CONTENT to the output, escaping CONTENT as + necessary for HTML. */ +static void +print_title_tag (FILE *file, const char *name, const char *content) { - struct html_driver_ext *x = this->ext; - - assert (this->driver_open); - msg (VM (2), _("%s: Beginning closing..."), this->name); - fn_close_ext (&x->file); - free (x->prologue_fn); - free (x->file.filename); - free (x); - msg (VM (3), _("%s: Finished closing."), this->name); - this->driver_open = 0; - - return 1; + if (content != NULL) + { + fprintf (file, "<%s>", name); + escape_string (file, content, " ", " - "); + fprintf (file, "\n", name); + } } - -/* Link the image contained in FILENAME to the - HTML stream in file F. */ -static int -link_image (struct file_ext *f, char *filename) +static void +html_destroy (struct output_driver *driver) { - fprintf (f->file, - "", filename); + struct html_driver *html = html_driver_cast (driver); - if (ferror (f->file)) - return 0; - - return 1; + if (html->file != NULL) + { + if (!html->bare) + fprintf (html->file, + "\n" + "\n" + "\n"); + fn_close (html->handle, html->file); + } + free (html->chart_file_name); + fh_unref (html->handle); + free (html); } - -/* Generic option types. */ -enum +static void +html_submit (struct output_driver *driver, + const struct output_item *output_item) { - boolean_arg = -10, - string_arg, - nonneg_int_arg -}; + struct html_driver *html = html_driver_cast (driver); -/* All the options that the HTML driver supports. */ -static struct outp_option option_tab[] = -{ - /* *INDENT-OFF* */ - {"output-file", 1, 0}, - {"prologue-file", string_arg, 0}, - {"", 0, 0}, - /* *INDENT-ON* */ -}; -static struct outp_option_info option_info; + if (is_table_item (output_item)) + { + struct table_item *table_item = to_table_item (output_item); + html_output_table (html, table_item); + } +#ifdef HAVE_CAIRO + else if (is_chart_item (output_item) && html->chart_file_name != NULL) + { + struct chart_item *chart_item = to_chart_item (output_item); + char *file_name; + + file_name = xr_draw_png_chart (chart_item, html->chart_file_name, + html->chart_cnt++, + &html->fg, + &html->bg + ); + if (file_name != NULL) + { + const char *title = chart_item_get_title (chart_item); + fprintf (html->file, "\"chart:", + file_name, title ? title : _("No description")); + free (file_name); + } + } +#endif /* HAVE_CAIRO */ + else if (is_text_item (output_item)) + { + struct text_item *text_item = to_text_item (output_item); + const char *s = text_item_get_text (text_item); + + switch (text_item_get_type (text_item)) + { + case TEXT_ITEM_PAGE_TITLE: + break; + + case TEXT_ITEM_TITLE: + { + int level = MIN (5, output_get_group_level ()) + 1; + char tag[3] = { 'H', level + '1', '\0' }; + print_title_tag (html->file, tag, s); + } + break; + + case TEXT_ITEM_SYNTAX: + fprintf (html->file, "
");
+          escape_string (html->file, s, " ", "
"); + fprintf (html->file, "
\n"); + break; + + case TEXT_ITEM_LOG: + print_title_tag (html->file, "pre", s); /* should be

*/ + break; + } + } + else if (is_message_item (output_item)) + { + const struct message_item *message_item = to_message_item (output_item); + char *s = msg_to_string (message_item_get_msg (message_item)); + print_title_tag (html->file, "p", s); + free (s); + } +} +/* Write TEXT to file F, escaping characters as necessary for HTML. Spaces are + replaced by SPACE, which should be " " or " " New-lines are replaced by + NEWLINE, which might be "
" or "\n" or something else appropriate. */ static void -html_option (struct outp_driver *this, const char *key, const struct string *val) +escape_string (FILE *file, const char *text, + const char *space, const char *newline) { - struct html_driver_ext *x = this->ext; - int cat, subcat; - - cat = outp_match_keyword (key, option_tab, &option_info, &subcat); - switch (cat) + for (;;) { - case 0: - msg (SE, _("Unknown configuration parameter `%s' for HTML device " - "driver."), key); - break; - case 1: - free (x->file.filename); - x->file.filename = xstrdup (ds_c_str (val)); - break; - case string_arg: - { - char **dest; - switch (subcat) - { - case 0: - dest = &x->prologue_fn; - break; - default: - assert (0); - abort (); - } - if (*dest) - free (*dest); - *dest = xstrdup (ds_c_str (val)); - } - break; - default: - assert (0); + char c = *text++; + switch (c) + { + case 0: + return; + case '\n': + fputs (newline, file); + break; + case '&': + fputs ("&", file); + break; + case '<': + fputs ("<", file); + break; + case '>': + fputs (">", file); + break; + case ' ': + fputs (space, file); + break; + case '"': + fputs (""", file); + break; + default: + putc (c, file); + break; + } } } -/* Variables for the prologue. */ -struct html_variable - { - const char *key; - const char *value; - }; - -static struct html_variable *html_var_tab; - -/* Searches html_var_tab for a html_variable with key KEY, and returns - the associated value. */ -static const char * -html_get_var (const char *key) +static void +escape_tag (FILE *file, const char *tag, + const char *text, const char *space, const char *newline) { - struct html_variable *v; + if (!text || !*text) + return; - for (v = html_var_tab; v->key; v++) - if (!strcmp (key, v->key)) - return v->value; - return NULL; + fprintf (file, "<%s>", tag); + escape_string (file, text, space, newline); + fprintf (file, "", tag); } -/* Writes the HTML prologue to file F. */ -static int -postopen (struct file_ext *f) +static const char * +border_to_css (int border) { - static struct html_variable dict[] = + switch (border) { - {"generator", 0}, - {"date", 0}, - {"user", 0}, - {"host", 0}, - {"title", 0}, - {"subtitle", 0}, - {0, 0}, - }; - char login[128], host[128]; - time_t curtime; - struct tm *loctime; - - struct outp_driver *this = f->param; - struct html_driver_ext *x = this->ext; - - char *prologue_fn = fn_search_path (x->prologue_fn, config_path, NULL); - FILE *prologue_file; - - char *buf = NULL; - size_t buf_size = 0; - - if (prologue_fn == NULL) - { - msg (IE, _("Cannot find HTML prologue. The use of `-vv' " - "on the command line is suggested as a debugging aid.")); - return 0; - } + case TABLE_STROKE_NONE: + return NULL; - msg (VM (1), _("%s: %s: Opening HTML prologue..."), this->name, prologue_fn); - prologue_file = fopen (prologue_fn, "rb"); - if (prologue_file == NULL) - { - fclose (prologue_file); - free (prologue_fn); - msg (IE, "%s: %s", prologue_fn, strerror (errno)); - goto error; - } + case TABLE_STROKE_SOLID: + return "solid"; - dict[0].value = version; + case TABLE_STROKE_DASHED: + return "dashed"; - curtime = time (NULL); - loctime = localtime (&curtime); - dict[1].value = asctime (loctime); - { - char *cp = strchr (dict[1].value, '\n'); - if (cp) - *cp = 0; - } - - if (getenv ("LOGNAME") != NULL) - str_copy_rpad (login, sizeof login, getenv ("LOGNAME")); - else if (getlogin_r (login, sizeof login)) - strcpy (login, _("nobody")); - dict[2].value = login; - -#ifdef HAVE_UNISTD_H - if (gethostname (host, 128) == -1) - { - if (errno == ENAMETOOLONG) - host[127] = 0; - else - strcpy (host, _("nowhere")); - } -#else - strcpy (host, _("nowhere")); -#endif - dict[3].value = host; + case TABLE_STROKE_THICK: + return "thick solid"; - dict[4].value = outp_title ? outp_title : ""; - dict[5].value = outp_subtitle ? outp_subtitle : ""; + case TABLE_STROKE_THIN: + return "thin solid"; - html_var_tab = dict; - while (-1 != getline (&buf, &buf_size, prologue_file)) - { - char *buf2; - int len; - - if (strstr (buf, "!!!")) - continue; - - { - char *cp = strstr (buf, "!title"); - if (cp) - { - if (outp_title == NULL) - continue; - else - *cp = '\0'; - } - } - - { - char *cp = strstr (buf, "!subtitle"); - if (cp) - { - if (outp_subtitle == NULL) - continue; - else - *cp = '\0'; - } - } - - /* PORTME: Line terminator. */ - buf2 = fn_interp_vars (buf, html_get_var); - len = strlen (buf2); - fwrite (buf2, len, 1, f->file); - if (buf2[len - 1] != '\n') - putc ('\n', f->file); - free (buf2); - } - if (ferror (f->file)) - msg (IE, _("Reading `%s': %s."), prologue_fn, strerror (errno)); - fclose (prologue_file); + case TABLE_STROKE_DOUBLE: + return "double"; - free (prologue_fn); - free (buf); - - if (ferror (f->file)) - goto error; - - msg (VM (2), _("%s: HTML prologue read successfully."), this->name); - return 1; + default: + return NULL; + } -error: - msg (VM (1), _("%s: Error reading HTML prologue."), this->name); - return 0; } -/* Writes the HTML epilogue to file F. */ -static int -preclose (struct file_ext *f) +struct css_style { - fprintf (f->file, - "\n" - "\n" - "\n"); - - if (ferror (f->file)) - return 0; - return 1; -} + FILE *file; + int n_styles; +}; -static int -html_open_page (struct outp_driver *this) +static struct css_style * +style_start (FILE *file) { - struct html_driver_ext *x = this->ext; - - assert (this->driver_open && this->page_open == 0); - x->sequence_no++; - if (!fn_open_ext (&x->file)) - { - if (errno) - msg (ME, _("HTML output driver: %s: %s"), x->file.filename, - strerror (errno)); - return 0; - } - - if (!ferror (x->file.file)) - this->page_open = 1; - return !ferror (x->file.file); + struct css_style *cs = XMALLOC (struct css_style); + cs->file = file; + cs->n_styles = 0; + fputs (" style=\"", file); + return cs; } -static int -html_close_page (struct outp_driver *this) +static void +style_end (struct css_style *cs) { - struct html_driver_ext *x = this->ext; - - assert (this->driver_open && this->page_open); - this->page_open = 0; - return !ferror (x->file.file); + fputs ("\"", cs->file); + free (cs); } -static void output_tab_table (struct outp_driver *, struct tab_table *); +static void +put_style (struct css_style *st, const char *name, const char *value) +{ + if (st->n_styles++ > 0) + fputs ("; ", st->file); + fprintf (st->file, "%s: %s", name, value); +} static void -html_submit (struct outp_driver *this, struct som_entity *s) +put_border (struct css_style *st, int style, const char *border_name) { - extern struct som_table_class tab_table_class; - struct html_driver_ext *x = this->ext; - - assert (this->driver_open && this->page_open); - if (x->sequence_no == 0 && !html_open_page (this)) + const char *css = border_to_css (style); + if (css) { - msg (ME, _("Cannot open first page on HTML device %s."), this->name); - return; + if (st->n_styles++ > 0) + fputs ("; ", st->file); + fprintf (st->file, "border-%s: %s", border_name, css); } +} - assert ( s->class == &tab_table_class ) ; - - switch (s->type) +static void +put_tfoot (struct html_driver *html, const struct table *t, bool *tfoot) +{ + if (!*tfoot) { - case SOM_TABLE: - output_tab_table ( this, (struct tab_table *) s->ext); - break; - case SOM_CHART: - link_image( &x->file, ((struct chart *)s->ext)->filename); - break; - default: - assert(0); - break; + fputs ("\n", html->file); + fputs ("\n", html->file); + fprintf (html->file, "\n", table_nc (t)); + *tfoot = true; } - + else + fputs ("\n
", html->file); } -/* Write string S of length LEN to file F, escaping characters as - necessary for HTML. */ static void -escape_string (FILE *f, char *s, int len) +html_put_footnote_markers (struct html_driver *html, + const struct footnote **footnotes, + size_t n_footnotes) { - char *ep = &s[len]; - char *bp, *cp; - - for (bp = cp = s; bp < ep; bp = cp) + if (n_footnotes > 0) { - while (cp < ep && *cp != '&' && *cp != '<' && *cp != '>' && *cp) - cp++; - if (cp > bp) - fwrite (bp, 1, cp - bp, f); - if (cp < ep) - switch (*cp++) - { - case '&': - fputs ("&", f); - break; - case '<': - fputs ("<", f); - break; - case '>': - fputs (">", f); - break; - case 0: - break; - default: - assert (0); - } + fputs ("", html->file); + for (size_t i = 0; i < n_footnotes; i++) + { + const struct footnote *f = footnotes[i]; + + if (i > 0) + putc (',', html->file); + escape_string (html->file, f->marker, " ", "
"); + } + fputs ("
", html->file); } } - -/* Write table T to THIS output driver. */ + static void -output_tab_table (struct outp_driver *this, struct tab_table *t) +html_put_table_item_text (struct html_driver *html, + const struct table_item_text *text) { - struct html_driver_ext *x = this->ext; - - if (t->nr == 1 && t->nc == 1) - { - fputs ("

", x->file.file); - if (!ls_empty_p (t->cc)) - escape_string (x->file.file, ls_c_str (t->cc), ls_length (t->cc)); - fputs ("

\n", x->file.file); - - return; - } + escape_string (html->file, text->content, " ", "
"); + html_put_footnote_markers (html, text->footnotes, text->n_footnotes); +} - fputs ("\n", x->file.file); - - if (!ls_empty_p (&t->title)) +static void +html_put_table_item_layers (struct html_driver *html, + const struct table_item_layers *layers) +{ + for (size_t i = 0; i < layers->n_layers; i++) { - fprintf (x->file.file, " \n \n \n", x->file.file); + if (i) + fputs ("
\n", html->file); + + const struct table_item_layer *layer = &layers->layers[i]; + escape_string (html->file, layer->content, " ", "
"); + html_put_footnote_markers (html, layer->footnotes, layer->n_footnotes); } - - { - int r; - unsigned char *ct = t->ct; - - for (r = 0; r < t->nr; r++) - { - int c; - - fputs (" \n", x->file.file); - for (c = 0; c < t->nc; c++, ct++) - { - struct fixed_string *cc; - int tag; - char header[128]; - char *cp; - struct tab_joined_cell *j = NULL; - - cc = t->cc + c + r * t->nc; - if (*ct & TAB_JOIN) - { - j = (struct tab_joined_cell *) ls_c_str (cc); - cc = &j->contents; - if (j->x1 != c || j->y1 != r) - continue; - } - - if (r < t->t || r >= t->nr - t->b - || c < t->l || c >= t->nc - t->r) - tag = 'H'; - else - tag = 'D'; - cp = stpcpy (header, " x2 - j->x1 > 1) - cp = spprintf (cp, " COLSPAN=%d", j->x2 - j->x1); - if (j->y2 - j->y1 > 1) - cp = spprintf (cp, " ROWSPAN=%d", j->y2 - j->y1); - - cc = &j->contents; - } - - strcpy (cp, ">"); - fputs (header, x->file.file); - - if ( ! (*ct & TAB_EMPTY) ) - { - char *s = ls_c_str (cc); - size_t l = ls_length (cc); - - while (l && isspace ((unsigned char) *s)) - { - l--; - s++; - } - - escape_string (x->file.file, s, l); - } - - fprintf (x->file.file, "\n", tag); - } - fputs (" \n", x->file.file); - } - } - - fputs ("
", t->nc); - escape_string (x->file.file, ls_c_str (&t->title), - ls_length (&t->title)); - fputs ("
\n\n", x->file.file); } static void -html_initialise_chart(struct outp_driver *d UNUSED, struct chart *ch) +html_output_table (struct html_driver *html, const struct table_item *item) { + const struct table *t = table_item_get_table (item); + bool tfoot = false; + int y; - FILE *fp; + fputs ("\n", html->file); - make_unique_file_stream(&fp, &ch->filename); + const struct table_item_text *caption = table_item_get_caption (item); + if (caption) + { + put_tfoot (html, t, &tfoot); + html_put_table_item_text (html, caption); + } + const struct footnote **f; + size_t n_footnotes = table_collect_footnotes (item, &f); -#ifdef NO_CHARTS - ch->lp = 0; -#else - ch->pl_params = pl_newplparams(); - ch->lp = pl_newpl_r ("png", 0, fp, stderr, ch->pl_params); -#endif + for (size_t i = 0; i < n_footnotes; i++) + { + put_tfoot (html, t, &tfoot); + escape_tag (html->file, "sup", f[i]->marker, " ", "
"); + escape_string (html->file, f[i]->content, " ", "
"); + } + free (f); + if (tfoot) + { + fputs ("\n", html->file); + fputs ("\n", html->file); + fputs ("\n", html->file); + } -} + const struct table_item_text *title = table_item_get_title (item); + const struct table_item_layers *layers = table_item_get_layers (item); + if (title || layers) + { + fputs ("\n", html->file); + } -static void -html_finalise_chart(struct outp_driver *d UNUSED, struct chart *ch) -{ - free(ch->filename); -} + fputs ("\n", html->file); + for (y = 0; y < table_nr (t); y++) + { + int x; + + fputs ("\n", html->file); + for (x = 0; x < table_nc (t);) + { + struct table_cell cell; + const char *tag; + + table_get_cell (t, x, y, &cell); + if (x != cell.d[TABLE_HORZ][0] || y != cell.d[TABLE_VERT][0]) + goto next_1; + + /* output or . */ + fprintf (html->file, "\n", tag); + + next_1: + x = cell.d[TABLE_HORZ][1]; + } + fputs ("\n", html->file); + } + fputs ("\n", html->file); + fputs ("
", html->file); + if (title) + html_put_table_item_text (html, title); + if (title && layers) + fputs ("
\n", html->file); + if (layers) + html_put_table_item_layers (html, layers); + fputs ("
or tag. */ + bool is_header = (y < table_ht (t) + || y >= table_nr (t) - table_hb (t) + || x < table_hl (t) + || x >= table_nc (t) - table_hr (t)); + tag = is_header ? "th" : "td"; + fprintf (html->file, "<%s", tag); + + struct css_style *style = style_start (html->file); + enum table_halign halign = table_halign_interpret ( + cell.style->cell_style.halign, cell.options & TAB_NUMERIC); + + switch (halign) + { + case TABLE_HALIGN_RIGHT: + put_style (style, "text-align", "right"); + break; + case TABLE_HALIGN_CENTER: + put_style (style, "text-align", "center"); + break; + default: + /* Do nothing */ + break; + } + + if (cell.style->cell_style.valign != TABLE_VALIGN_TOP) + { + put_style (style, "vertical-align", + (cell.style->cell_style.valign == TABLE_VALIGN_BOTTOM + ? "bottom" : "middle")); + } + + int colspan = table_cell_colspan (&cell); + int rowspan = table_cell_rowspan (&cell); + + if (html->borders) + { + /* Cell borders. */ + struct cell_color color; + + int top = table_get_rule (t, TABLE_VERT, x, y, &color); + put_border (style, top, "top"); + + if (y + rowspan == table_nr (t)) + { + int bottom = table_get_rule (t, TABLE_VERT, x, y + rowspan, + &color); + put_border (style, bottom, "bottom"); + } + + int left = table_get_rule (t, TABLE_HORZ, x, y, &color); + put_border (style, left, "left"); + + if (x + colspan == table_nc (t)) + { + int right = table_get_rule (t, TABLE_HORZ, x + colspan, y, + &color); + put_border (style, right, "right"); + } + } + style_end (style); + + if (colspan > 1) + fprintf (html->file, " colspan=\"%d\"", colspan); + + if (rowspan > 1) + fprintf (html->file, " rowspan=\"%d\"", rowspan); + + putc ('>', html->file); + + /* Output cell contents. */ + const char *s = cell.text; + if (cell.options & TAB_FIX) + escape_tag (html->file, "tt", s, " ", "
"); + else + { + s += strspn (s, CC_SPACES); + escape_string (html->file, s, " ", "
"); + } + + if (cell.n_subscripts) + { + fputs ("", html->file); + for (size_t i = 0; i < cell.n_subscripts; i++) + { + if (i) + putc (',', html->file); + escape_string (html->file, cell.subscripts[i], + " ", "
"); + } + fputs ("
", html->file); + } + if (cell.superscript) + escape_tag (html->file, "sup", cell.superscript, " ", "
"); + html_put_footnote_markers (html, cell.footnotes, cell.n_footnotes); + + /* output
\n\n", html->file); +} -/* HTML driver class. */ -struct outp_class html_class = -{ - "html", - 0xfaeb, - 1, - - html_open_global, - html_close_global, - NULL, - - html_preopen_driver, - html_option, - html_postopen_driver, - html_close_driver, - - html_open_page, - html_close_page, - - html_submit, - - NULL, - NULL, - NULL, - - NULL, - NULL, - NULL, - NULL, - - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - - html_initialise_chart, - html_finalise_chart +struct output_driver_factory html_driver_factory = + { "html", "pspp.html", html_create }; -}; +static const struct output_driver_class html_driver_class = + { + "html", + html_destroy, + html_submit, + NULL, + };